Every Raven project has three environments:
Project
├── development (the default, everywhere)
├── staging
└── production
They are fixed — you cannot add a fourth — and they isolate credentials and data from each other completely.
What is isolated
| Isolated by environment | |
|---|---|
| API keys | ✅ A key belongs to exactly one environment |
| Chat tokens | ✅ Inherited from the key that minted them |
| Rooms | ✅ Including their names |
| Conversations | ✅ Including their names |
| Webhook endpoints | ✅ A staging endpoint never receives production events |
| Connections, errors, usage | ✅ Tagged, so metrics can be read per environment |
What is not isolated: the project itself, its name, and who can administer it. Environments separate traffic, not ownership.
Creating a key
raven keys create --name backend --environment production✔ API key created for the production environment.
Save this key now. It will not be shown again.
rvk_prod_8Kd2nQxwYtLm.aG9wZXlvdWFyZWhhdmluZ2Fsb3ZlbHlkYXk
This is a production key. Keep it server-side — never in an app bundle
or a committed file.
--environment accepts dev/development, stg/staging,
prod/production, in any case. An unrecognised value is refused rather
than defaulted: quietly issuing a development key to someone who typed
--environment prd is the worst available outcome.
Omitting it gives you development. A wrong guess should land somewhere harmless.
Reading a key at a glance
The environment is written into the key's public half:
rvk_dev_8Kd2nQxwYtLm development
rvk_stg_2mNpQ4rZ7xKw staging
rvk_prod_9YtL1nB2xQcR production
This is decoration for humans, not a claim. The server reads the environment from the key's database row, never from its text — editing the prefix changes nothing, and keys issued before this existed have no segment and keep working exactly as they did.
How an environment is decided
Never by the request. Always by the credential.
API key ──carries──► environment
│
│ mints
▼
Chat token ──signed `env` claim──► same environment
│
▼
Browser
A backend holding a development key cannot mint a production chat token,
and a browser cannot reach production data by changing a request field —
for the same reason it cannot forge senderId. Both are signed claims,
not parameters.
Dashboard and CLI routes are the exception: there the caller is a
logged-in developer, so they pass ?environment= and it defaults to
development. Those routes are authenticated by a session, not by a key,
so there is no key to read it from.
What isolation looks like in practice
The same room name in two environments is two different rooms:
# Development
curl -H "Authorization: Bearer $DEV_KEY" -d '{"name":"lobby"}' .../v1/rooms
# Production — not a conflict
curl -H "Authorization: Bearer $PROD_KEY" -d '{"name":"lobby"}' .../v1/roomsAnd a key cannot see across the boundary, even holding an exact id:
curl -H "Authorization: Bearer $DEV_KEY" .../v1/rooms/<the production room id>
# 404 { "code": "RAVEN_ROOM_NOT_FOUND" }That is 404, not 403, deliberately. A 403 would confirm the id is real somewhere, which is exactly what someone probing ids wants to learn. The same rule applies to conversations, messages, and RTC tokens.
Webhooks
An endpoint is registered for one environment:
POST /v1/projects/{id}/webhooks
{ "url": "https://api.example.com/hooks", "environment": "PRODUCTION" }and every delivered event says which environment it came from:
{
"id": "evt_9f2c41ab77e0",
"type": "message.created",
"projectId": "…",
"environment": "PRODUCTION",
"createdAt": "2026-08-19T09:31:04.221Z",
"data": { }
}Getting this wrong is worse than a missed delivery — it means real customer data posted to whatever URL someone pointed at their laptop while testing. So endpoints are matched on environment before anything is queued, not filtered afterwards.
Migrating an existing project
Everything that existed before environments is development. That is true of API keys, rooms, conversations, webhook endpoints and stored telemetry, and nothing changed behaviourally: a project that never mentions environments behaves exactly as it did.
To adopt them:
raven keys create --environment productionfor your production backend.- Register a production webhook endpoint if you use webhooks.
- Swap the key in your production deployment.
- Revoke the old key once nothing is using it.
Your development traffic is unaffected throughout, because it was already in the development environment.
A note on the word "environment"
Raven uses it for a project's environment. It is unrelated to NODE_ENV
or how the Raven server itself was deployed — a single Raven deployment
serves all three project environments. Where the codebase needs the
deployment's own mode (the webhook SSRF guard, for one) it is called
deploymentEnv to keep the two apart.