Variables and secrets for local API testing
How to handle API testing variables, local secrets, environment files, RQB_* CI variables, and dry runs in a local-first API workspace.
Local API testing becomes fragile when every request hides its own environment values. The base URL is copied in one place, the token is pasted somewhere else, and a coding agent has to guess which value is safe to use.
Reqbook treats variables as part of the API contract, but keeps secrets out of Markdown.

Variables are part of the contract
If an endpoint needs baseUrl, authToken, and userId, that should be visible in the spec.
GET {{baseUrl}}/users/:id
Authorization: Bearer {{authToken}}
Accept: application/json
The spec should not hide those inputs. It should make them obvious so humans, CI, and agents know what the request needs before it runs.
That is especially important for coding agents. A missing variable is not a reason to guess. It is a reason to inspect the environment sources or ask for the correct value.
The resolution order matters
Reqbook resolves variables in a strict priority order:
| Priority | Source |
|---|---|
| 1 | Pipeline step capture |
| 2 | CLI --var flag |
| 3 | Endpoint frontmatter |
| 4 | _shared/env.md for the selected env |
| 5 | .env.local |
| 6 | RQB_* OS environment variables |
That order lets a flow capture userId from a real response and use it later, while still allowing humans and CI to provide defaults.
For example:
RQB_USER_ID=from-os rqb exec api-docs/apis/users/get-user.md --var userId=from-cli
The request uses from-cli because an explicit CLI override is higher priority than an OS environment variable.
Secrets do not belong in Markdown
Use _shared/env.md for non-secret values:
# Environments
## dev
```yaml
baseUrl: http://localhost:8082
pageSize: 20
```
## staging
```yaml
baseUrl: https://staging.example.com
pageSize: 20
```
Use .env.local for local secrets:
authToken=local-development-token
webhookSecret=local-development-secret
Use RQB_* environment variables in CI:
env:
RQB_AUTH_TOKEN: ${{ secrets.API_TOKEN }}
RQB_BASE_URL: https://staging.example.com
The Markdown file remains reviewable. The secrets stay in the environment that owns them.
Use dry runs before CI
Before sending a request, check the rendered request:
rqb exec api-docs/apis/users/get-user.md --env=dev --dry-run
A dry run is useful when:
- a variable is missing,
- a value is coming from the wrong source,
- a coding agent needs to inspect the final request,
- CI is failing before the API call is made.
The goal is simple: variable problems should fail early, clearly, and without leaking secret values.
Name variables for readers, not only machines
Good variable names make a contract easier to inspect in a pull request. Prefer names that explain the role of the value:
| Better | Weaker |
|---|---|
authToken | token |
workspaceId | id |
checkoutSessionId | session |
webhookSecret | secret |
baseUrl | url |
This matters because the same spec is read by several audiences. A developer wants to know which value is missing. A reviewer wants to understand whether the contract is safe. A coding agent needs enough semantic context to choose the next action.
Short names are convenient until a flow captures three different IDs. Descriptive names prevent accidental injection of the wrong value into a later step.
Keep environment files boring
The shared environment file should hold non-secret defaults:
baseUrl: http://localhost:8082
pageSize: 20
workspaceName: demo-workspace
Avoid putting values in _shared/env.md that change per developer or per CI run. Those belong in .env.local, CLI --var, captured pipeline values, or RQB_* environment variables.
A boring environment file has two advantages:
- it is safe to review and commit;
- it gives agents stable defaults without exposing secrets.
If a teammate cannot read the environment file in a pull request, the file is doing too much.
How agents should handle missing variables
When a variable is missing, the agent should not invent it. A good agent workflow is:
- Run
rqb_varsorrqb exec --dry-run. - Read the missing variable names.
- Check
_shared/env.mdand non-secret frontmatter. - Ask the user for secrets or environment-specific values.
- Run the spec only after variables resolve.
This is where explicit contracts beat copied curl commands. A curl command with a placeholder token often gets silently edited. A Reqbook spec can fail before the request leaves the machine.
Example missing-variable failure:
api-docs/apis/users/get-user.md: unresolved variable "authToken"
Fix: define authToken in .env.local, pass --var authToken=..., or set RQB_AUTH_TOKEN.
That message gives a human or agent the next safe step.
CI secret handling checklist
Before adding API specs to CI, check these items:
.env.localis in.gitignore.- No token-like values appear in
api-docs/. - CI secrets use the
RQB_prefix. - Specs can run with
--dry-runwithout leaking values. - Failing logs do not print raw secret values.
- The staging
baseUrllives in CI config or_shared/env.md, not scattered across specs.
This checklist is not just security hygiene. It also improves reliability. When every CI secret is injected the same way, failures become easier to debug and easier for agents to reason about.
Prefer captures over manual IDs in flows
For multi-step tests, do not pass IDs manually when the previous response can produce them.
Instead of:
rqb flow api-docs/flows/onboarding.md --var userId=usr_123
Prefer:
1. **Create user** -> `apis/users/post-users.md`
- Capture: `response.body.id` as `userId`
2. **Get profile** -> `apis/users/get-user-by-id.md`
- Inject: `userId`
Captured variables have the highest priority because they reflect the live response from the running flow. That is exactly what an onboarding or checkout test should verify.
Use the UI to inspect, not to hide state
The Reqbook browser UI is useful for inspecting which environment is selected and which endpoint is about to run. It should not become a hidden state store that only exists in one browser session.
If a value matters to the team, put it in the right file or environment source. Use the UI to override values temporarily while debugging, then move stable values back into _shared/env.md, .env.local, CI secrets, or pipeline captures.
That keeps local API testing repeatable. A teammate, CI job, or coding agent can run the same contract without recreating a private browser state.
For the full CI pattern, read CI API testing with Markdown specs. For a repo-level workflow, read API docs as code.