Every project has isolated test and live environments - keys, webhooks, events, and rate limits never cross over.
Every project has two environments - test and live - that are fully isolated from each other. They exist so you can develop, integrate, and load-test without touching production traffic.
The two environments share a project's name and region but nothing else:
| Resource | Shared | Isolated per environment |
|---|---|---|
| API keys | - | yes - mrnd_test_ vs mrnd_live_ |
| Webhook endpoints | - | yes - registered separately |
| Events and logs | - | yes - separate retention and history |
| Rate limits | - | yes - test traffic can't throttle live |
| Project name / region | yes | - |
| Member permissions | - | yes - roles can differ per environment |
A test key literally cannot resolve live resources - they don't exist in its view of the world.
The intended lifecycle for a change:
test - build against mrnd_test_ keys with no risk to production.test.live (manually, or via the Environments API / Terraform).mrnd_live_ keys and flip your config flag.# test
curl https://api.meridian.dev/v2/projects \
-H "Authorization: Bearer $MERIDIAN_TEST_KEY"
# live - identical path, different key
curl https://api.meridian.dev/v2/projects \
-H "Authorization: Bearer $MERIDIAN_LIVE_KEY"The environment is selected entirely by the API key - there is no ?env= parameter and no way to mix them in one request.
localhost tunnels like localhost:3000 or ngrok.environment field and volume differ.Environments themselves are resources - useful for infrastructure-as-code setups:
curl https://api.meridian.dev/v2/projects/prj_9kLm2xVb7nQe/environments \
-H "Authorization: Bearer $MERIDIAN_API_KEY"{
"object": "list",
"data": [
{
"id": "env_2hF8kLm4xQ",
"object": "environment",
"name": "test",
"status": "active",
"event_retention_days": 7
},
{
"id": "env_7dR1mPx9vW",
"object": "environment",
"name": "live",
"status": "active",
"event_retention_days": 90
}
]
}