Getting started
Testing against ZevAuth
Minting tokens without passwords, reading captured email, resetting users, and forcing failures.
Test environments have a set of endpoints that live environments do not. They exist so an integration test can set up the state it needs, read what we would have emailed, and tear itself down afterwards.
Everything on this page needs a test secret key (sk_test_…) and is
refused outright in live, so none of it can run against real users.
A token without a password
/v1/sandbox/users/{userId}/tokens The usual way to get a token is to create a user with a password and sign them in. That works, and it means every test suite stores a credential and spends its rate limit exercising our sign-in flow on the way to testing something else.
This mints a session directly:
curl -X POST https://api.zevauth.net/v1/sandbox/users/user_.../tokens \
-H "Authorization: Bearer sk_test_..."
{
"accessToken": "eyJhbGciOiJSUzI1NiIs...",
"refreshToken": "rt_...",
"expiresIn": 3600
}
It returns exactly what sign-in returns, and the session is a real one: it appears in the user’s session list, it can be revoked, and it expires on schedule. A test that passes against this token proves the same thing it would have proved against a token from the sign-in form.
The one difference is that the session records provider: "sandbox", so
your sign-in log can tell a test harness apart from a person.
// The whole setup for a test that needs an authenticated caller.
const user = await zevauth.users.create({ email: 'ada@example.com' });
const { accessToken } = await zevauth.sandbox.mintToken(user.id);
await fetch('/api/my-endpoint', {
headers: { Authorization: `Bearer ${accessToken}` },
});
A blocked user is refused, as sign-in refuses them. An unknown user is a 404.
Reading the email we captured
/v1/sandbox/emails Test environments capture messages instead of delivering them, which is
what "emailDelivery": "captured" on
GET /v1/environment means. This is how you read
what was captured, so flows that depend on a link we sent can be tested
rather than only built: email verification, magic link sign in, password
reset.
curl "https://api.zevauth.net/v1/sandbox/emails?to=ada@example.com" \
-H "Authorization: Bearer sk_test_..."
{
"data": [
{
"id": "cem_...",
"to": "ada@example.com",
"from": "no-reply@zevauth.com",
"subject": "Verify your email",
"kind": "email_verification",
"code": "418293",
"actionUrl": "https://accounts.yourapp.com/verify?token=...",
"html": "…",
"text": "…",
"createdAt": "2026-08-28T18:00:00.000Z"
}
]
}
code and actionUrl are stored as their own fields so a test never has
to parse our HTML. An assertion against a template breaks the next time
somebody changes the design; an assertion against code does not.
Newest first. ?to= filters by recipient and is case insensitive,
?kind= filters by message type, and ?limit= defaults to 25 with a
maximum of 100.
const { data } = await zevauth.sandbox.emails({ to: 'ada@example.com' });
const code = data[0].code;
await zevauth.auth.verifyEmail({ code });
Resetting between runs
/v1/sandbox/users Deletes every end user in the test environment, and with them their identities, sessions and refresh tokens.
curl -X DELETE https://api.zevauth.net/v1/sandbox/users \
-H "Authorization: Bearer sk_test_..."
{ "deleted": 42 }
Call it from CI between runs. Test environments cap the number of users they hold, so a suite that creates accounts and never clears them eventually cannot sign anybody up at all.
Forcing a failure
/v1/sandbox/simulations Lists the outcomes you can make this environment produce on demand, so your error paths get exercised without waiting for a real failure.
{
"environment": "env_test_...",
"outcomes": [
{
"outcome": "rate_limited",
"email": "you+zevauth_sim_rate_limited@example.com",
"header": { "X-Zevauth-Simulate": "rate_limited" }
}
]
}
Trigger one either by signing up with the address, or by sending the header on any request. The list is served at runtime rather than written down here, so it never drifts from what the API actually supports.
Getting the keys into your test config
You do not need to open the dashboard to wire this up. Given a secret
key, GET /v1/environment returns the
publishable key, so a provisioning script or a per-branch preview
environment can seed a frontend .env on its own. See
API keys.
Updated at, Wednesday, October 21, 2026