name: api-flow-tester
description: Use when the user wants to run, debug, create, or update multi-step HTTP API test flows, especially when later requests depend on values captured from earlier responses.
API Flow Tester
Use this skill for repeatable HTTP API flow testing against a running service.
When to Use
- Run an existing flow file step by step
- Preview a flow safely with dry-run before hitting the server
- Debug why a chained API flow fails
- Create a new flow from an endpoint sequence or API spec
- Update a flow after endpoint, header, body, or response changes
- Verify auth and resource handoff across multiple requests
- Run only part of a flow while debugging
- Validate expected failure cases such as
401, 403, or 422
Do not use this for a single one-off endpoint check unless the user explicitly wants a reusable flow artifact.
Repository Discovery
Before doing anything, inspect the repo and identify:
- Flow file location. Check user-provided paths first, then search in this order:
tests/flows/*/flow.md
flows/*/flow.md
docs/flows/*/flow.md
Prefer per-flow directories such as tests/flows/customer-login/flow.md.
- API reference. Prefer local files such as
openapi.json, openapi.yaml, or swagger.json. If none exist, probe common server routes such as /openapi.json, /api/openapi.json, or the user-provided docs URL. If no spec is available, use the repo's route definitions or existing tests.
- Base URL. Prefer the value inside the flow file, then user input, then project config or
.env files. If nothing is explicit, use a clearly stated default such as http://localhost:8080.
If any of these are ambiguous, state the assumption before executing.
Flow Format
Flow files are Markdown documents with:
- A flow title
- Base URL
- Optional OpenAPI URL
- Optional description
- Ordered
Step N sections
- Per-step method and path
- Optional headers
- Optional JSON body
- Optional expectations such as status or required response fields
- Optional captured variables that map variable names to JSON paths
Use {{variable_name}} placeholders in later steps. Read references/example-flow.md before creating a new file or normalizing an existing one.
For secret-backed flows, also read references/flow-test-env.example. For OTP or phone-based login flows, read references/example-otp-flow.md. For explicit negative-test patterns, read references/example-negative-flow.md.
If you create a new flow at tests/flows/<flow-name>/flow.md, also create tests/flows/<flow-name>/.env.example.
Supported expectation patterns:
Status
JSON path exists
JSON path not exists
JSON path equals
JSON path contains
JSON path matches regex
Header equals
Header contains
Sensitive Inputs
Flow files may live in the repository, so do not hardcode secrets or personal data into committed files.
Use this order of preference:
- Reuse existing environment variables or local secret files that are already ignored by git.
- Use placeholders such as
{{ADMIN_EMAIL}}, {{ADMIN_PASSWORD}}, {{TEST_PHONE_NUMBER}}, or {{OTP_CODE}} inside the flow file.
- Before execution, resolve those placeholders from an untracked environment-specific source such as
.flow.test.local.env, .flow.test.staging.env, .flow.test.production.env, or a per-flow secret file.
- If a required sensitive value is still missing or ambiguous, ask the user for it before running the flow.
Default secret file names to propose:
- Per-flow:
tests/flows/<flow-name>/flow.md
tests/flows/<flow-name>/.env.local
tests/flows/<flow-name>/.env.staging
tests/flows/<flow-name>/.env.production
tests/flows/<flow-name>/.env.example
- Repo-wide fallback:
.flow.test.local.env
.flow.test.staging.env
.flow.test.production.env
When editing repo files:
- Keep only placeholders in committed flow files.
- The
.env.example file is required for new secret-backed flows and must list every placeholder-backed input with blank or example-safe values only.
- If secret values will live in
.env, .env.*, or per-flow secret files, make sure the real secret files are ignored by git before writing them.
- Never replace placeholders with real secrets in the saved flow file.
- If the repo lacks an ignored local secret source, propose one and ask before creating it.
- If the ignore rule is missing, ask before editing a tracked
.gitignore, then add the required entries so .env and relevant .env.* files do not get committed.
- Do not ignore
.env.example; it must stay committable so users can discover the required keys safely.
- Do not put real secrets or secret-like example values in
.env.example; every value there must be blank or clearly safe to commit.
- If keys change in
.env, .env.*, or per-flow secret files, update .env.example in the same change so the committed example stays in sync.
- When creating a new local secret file, also offer to create a matching example file such as
.flow.test.env.example without real values.
Running a Flow
Supported execution modes:
- Full run: execute every step in order
- Dry run: resolve inputs and show each request without sending it
- Partial run: execute only selected steps or start from a selected step
- Read the flow file and parse each step in order.
- Build a session variable map for captured values and external input values.
- Resolve placeholders in path, headers, and body before each request.
- Decide the execution mode from the user request. If the user asks to preview, validate, inspect, or verify without side effects, use dry run.
- If the user asks to run only part of the flow, determine either:
- a single target step
- a start step and continue to the end
- explicit steps to skip
- For partial runs, make sure required inputs for skipped earlier steps still exist. Prefer existing env values or ask the user before running if a skipped step would have produced required captures.
- Execute requests sequentially when not in dry run. Use
curl -sS as the canonical request transport. Use jq for JSON extraction. If jq is unavailable or the extraction is too complex, use python3 only for parsing or extraction, not for sending requests.
- Validate expectations after each response.
- Stop on the first unexpected failure unless the user explicitly asks to continue.
Environment resolution rules:
- Determine the flow identifier from the directory name when the flow path is
<flow-name>/flow.md. Use kebab-case such as customer-login.
- Detect available per-flow environment files such as
./.env.local, ./.env.staging, and ./.env.production.
- If exactly one per-flow environment file exists, use it and state which environment was selected.
- If multiple per-flow environment files exist, ask the user which environment to use before executing the flow.
- If no per-flow environment file exists, fall back to repo-wide environment-specific secret files.
- If multiple repo-wide candidates exist, ask the user which one to use.
Environment selection SOP:
- When multiple environment files exist for the same flow, ask the user explicitly which one to use.
- Use a direct prompt such as:
Flow "customer-login" has environments: local, staging, production. Which one should I run?
- Do not guess the environment from the current branch, hostname, or prior conversation unless the user already specified it in the current request.
- If
production is chosen, ask for explicit confirmation again before sending any request with side effects.
For each step, report:
- Step name
- Request summary: method and path
- Execution mode when not doing a normal full run
- Response status
- Key assertion result
- Captured variable names
Do not print secrets in full. Redact bearer tokens, refresh tokens, API keys, and cookies in the summary.
In dry run mode, report:
- resolved method and path
- resolved headers with secrets redacted
- resolved body with secrets redacted
- resolved
curl command
- whether every placeholder was resolved
- whether the step is ready to execute
Prefer a single generated curl command per step for replay and debugging. Do not generate ad-hoc Python scripts to send HTTP requests unless curl cannot represent the request accurately.
Capturing Variables
When a step defines captured variables:
- Parse the response body as JSON.
- Extract each value from the declared JSON path.
- Store successful captures in the session variable map.
- Fail fast if a required capture is missing, and show the relevant response snippet plus the missing path.
If a response body is not JSON, say so explicitly and do not pretend extraction succeeded.
Assertions And Negative Tests
Treat non-2xx responses as valid only when the flow explicitly expects them.
Rules:
- If
Status is 401, 403, 404, 409, 422, or another non-2xx value, treat the step as a negative test step.
- For negative test steps, still validate all declared expectations.
- If the flow expects a non-2xx status and the server returns
2xx, mark the step as failed.
- If the flow expects
2xx and the server returns non-2xx, mark the step as failed.
- When negative-test intent is not clear, ask the user before rewriting the flow.
Example expectation block:
**Expect:**
- Status: 422
- JSON path exists: `.error.code`
- JSON path equals: `.error.code` -> `INVALID_PHONE_NUMBER`
- JSON path contains: `.error.message` -> `phone`
Partial Run Rules
When the user asks to run only part of the flow:
- Accept requests such as
run step 3, start from step 2, skip login, or only run get profile.
- Resolve step references by step number first, then by exact step name.
- If a later step depends on captured values from skipped steps, prefer env-backed placeholders or ask the user how to supply them.
- Do not silently invent missing captured values.
- Report clearly which steps were skipped and why.
Creating or Updating a Flow
- Inspect the current API reference and any related existing flows.
- Draft the flow using the documented format from the example reference.
- Keep the flow definition environment-agnostic when the same flow will run against multiple targets.
- If the new flow uses secret-backed or environment-backed placeholders, create a sibling
.env.example file in the same flow directory and include every required placeholder-backed key with blank or example-safe values only.
- If the flow will rely on
.env, .env.*, or per-flow secret files, check whether .gitignore already ignores those real secret files before writing or recommending them.
- If the ignore rule is missing, ask before editing a tracked
.gitignore, then add the necessary entries so real secret files stay untracked while .env.example remains committable.
- Keep
.env.example safe to commit by using blank values or obviously non-secret placeholders only.
- If secret-file keys change, update
.env.example in the same change so it remains an accurate contract for required inputs.
- If the user asked for an edit, summarize the intended changes before writing.
- If the flow needs credentials, phone numbers, OTPs, or other sensitive values, keep them as placeholders and define how they will be provided at runtime through
.env.<environment> files.
- Ask for approval before saving any new or modified flow file.
- After approval, write the file and optionally run it.
Never invent request or response fields when the spec or code does not support them. Mark assumptions clearly.
When to Ask the User
Ask before proceeding when:
- The base URL is unclear and multiple plausible targets exist
- The OpenAPI spec is missing locally and probing server routes could hit the wrong service
- Multiple
.env.<environment> files exist for the same flow and the user has not chosen one yet
- The flow may target
production or another live environment with real side effects
- A flow depends on credentials, phone numbers, OTP codes, or other sensitive values that are not already available from a safe local source
- A partial run skips earlier steps that would normally generate required captured values
- The repo does not yet have an ignored file for local flow secrets and creating one would change tracked files such as
.gitignore
- A non-2xx step could be either an expected negative test or an actual failure
Do not ask unnecessary implementation-detail questions when the repo or spec already answers them.
Failure Handling
- Server unreachable: report the base URL and the connection failure clearly.
- Unexpected non-2xx status: show the step, status, and a short response excerpt.
- Missing variable capture: stop and show which JSON path failed.
- Spec missing: continue if the flow is otherwise clear, but say validation is limited.
- Placeholder unresolved: stop before sending the request and show the missing variable name.
- Production target: require explicit user confirmation before executing any write action or any endpoint with business side effects.
Output Style
Keep the result compact and structured. A good run summary looks like:
Step 1: Login
- Request: POST /auth/login
- Response: 200
- Captured: access_token, user_id
Step 2: Get profile
- Request: GET /users/42
- Response: 200
- Assertions: email exists
For dry run or partial run, include a short preface such as:
Mode: dry run
Selected steps: 2-3
Secret source: tests/flows/customer-login/.env.staging
Recommended Conventions
- Committed flow files: keep under
tests/flows/<flow-name>/flow.md unless the repo already uses another convention
- Use
kebab-case for flow directory names, for example customer-login, admin-refresh-session, booking-create-and-pay
- Each committed flow should declare
Base URL explicitly, usually as {{BASE_URL}}
- Add
OpenAPI URL to the flow whenever the spec is served over HTTP or differs by environment, usually as {{OPENAPI_URL}}
- Do not hardcode
Environment into flow.md when the same flow can run against multiple targets
- Do not hardcode
Secrets File into flow.md unless the user explicitly wants an override
- Per-flow secret files should be the default:
tests/flows/<flow-name>/.env.local, tests/flows/<flow-name>/.env.staging, tests/flows/<flow-name>/.env.production
- Commit a per-flow example file when useful:
tests/flows/<flow-name>/.env.example
- Repo-wide secret files are fallback only:
.flow.test.local.env, .flow.test.staging.env, .flow.test.production.env
- Example secret files: prefer committed
*.env.example files with blank or placeholder values only
- Placeholder names: use uppercase snake case such as
{{ADMIN_EMAIL}}, {{TEST_PHONE_NUMBER}}, {{OTP_CODE}}
1---2name: codex-43description: <!-- Generated by scripts/sync.sh; source_sha256: 10c14c8622fb0e7d1deabcf80ad457b94c872beb7f75325cc19bd1a1b6a5eb09 -->4---5<!-- Generated by scripts/sync.sh; source_sha256: 10c14c8622fb0e7d1deabcf80ad457b94c872beb7f75325cc19bd1a1b6a5eb09 -->67---8name: api-flow-tester9description: Use when the user wants to run, debug, create, or update multi-step HTTP API test flows, especially when later requests depend on values captured from earlier responses.10---1112# API Flow Tester1314Use this skill for repeatable HTTP API flow testing against a running service.1516## When to Use1718- Run an existing flow file step by step19- Preview a flow safely with dry-run before hitting the server20- Debug why a chained API flow fails21- Create a new flow from an endpoint sequence or API spec22- Update a flow after endpoint, header, body, or response changes23- Verify auth and resource handoff across multiple requests24- Run only part of a flow while debugging25- Validate expected failure cases such as `401`, `403`, or `422`2627Do not use this for a single one-off endpoint check unless the user explicitly wants a reusable flow artifact.2829## Repository Discovery3031Before doing anything, inspect the repo and identify:32331. Flow file location. Check user-provided paths first, then search in this order:34 - `tests/flows/*/flow.md`35 - `flows/*/flow.md`36 - `docs/flows/*/flow.md`37 Prefer per-flow directories such as `tests/flows/customer-login/flow.md`.382. API reference. Prefer local files such as `openapi.json`, `openapi.yaml`, or `swagger.json`. If none exist, probe common server routes such as `/openapi.json`, `/api/openapi.json`, or the user-provided docs URL. If no spec is available, use the repo's route definitions or existing tests.393. Base URL. Prefer the value inside the flow file, then user input, then project config or `.env` files. If nothing is explicit, use a clearly stated default such as `http://localhost:8080`.4041If any of these are ambiguous, state the assumption before executing.4243## Flow Format4445Flow files are Markdown documents with:4647- A flow title48- Base URL49- Optional OpenAPI URL50- Optional description51- Ordered `Step N` sections52- Per-step method and path53- Optional headers54- Optional JSON body55- Optional expectations such as status or required response fields56- Optional captured variables that map variable names to JSON paths5758Use `{{variable_name}}` placeholders in later steps. Read [references/example-flow.md](./references/example-flow.md) before creating a new file or normalizing an existing one.5960For secret-backed flows, also read [references/flow-test-env.example](./references/flow-test-env.example). For OTP or phone-based login flows, read [references/example-otp-flow.md](./references/example-otp-flow.md). For explicit negative-test patterns, read [references/example-negative-flow.md](./references/example-negative-flow.md).6162If you create a new flow at `tests/flows/<flow-name>/flow.md`, also create `tests/flows/<flow-name>/.env.example`.6364Supported expectation patterns:6566- `Status`67- `JSON path exists`68- `JSON path not exists`69- `JSON path equals`70- `JSON path contains`71- `JSON path matches regex`72- `Header equals`73- `Header contains`7475## Sensitive Inputs7677Flow files may live in the repository, so do not hardcode secrets or personal data into committed files.7879Use this order of preference:80811. Reuse existing environment variables or local secret files that are already ignored by git.822. Use placeholders such as `{{ADMIN_EMAIL}}`, `{{ADMIN_PASSWORD}}`, `{{TEST_PHONE_NUMBER}}`, or `{{OTP_CODE}}` inside the flow file.833. Before execution, resolve those placeholders from an untracked environment-specific source such as `.flow.test.local.env`, `.flow.test.staging.env`, `.flow.test.production.env`, or a per-flow secret file.844. If a required sensitive value is still missing or ambiguous, ask the user for it before running the flow.8586Default secret file names to propose:8788- Per-flow:89 - `tests/flows/<flow-name>/flow.md`90 - `tests/flows/<flow-name>/.env.local`91 - `tests/flows/<flow-name>/.env.staging`92 - `tests/flows/<flow-name>/.env.production`93 - `tests/flows/<flow-name>/.env.example`94- Repo-wide fallback:95 - `.flow.test.local.env`96 - `.flow.test.staging.env`97 - `.flow.test.production.env`9899When editing repo files:100101- Keep only placeholders in committed flow files.102- The `.env.example` file is required for new secret-backed flows and must list every placeholder-backed input with blank or example-safe values only.103- If secret values will live in `.env`, `.env.*`, or per-flow secret files, make sure the real secret files are ignored by git before writing them.104- Never replace placeholders with real secrets in the saved flow file.105- If the repo lacks an ignored local secret source, propose one and ask before creating it.106- If the ignore rule is missing, ask before editing a tracked `.gitignore`, then add the required entries so `.env` and relevant `.env.*` files do not get committed.107- Do not ignore `.env.example`; it must stay committable so users can discover the required keys safely.108- Do not put real secrets or secret-like example values in `.env.example`; every value there must be blank or clearly safe to commit.109- If keys change in `.env`, `.env.*`, or per-flow secret files, update `.env.example` in the same change so the committed example stays in sync.110- When creating a new local secret file, also offer to create a matching example file such as `.flow.test.env.example` without real values.111112## Running a Flow113114Supported execution modes:115116- Full run: execute every step in order117- Dry run: resolve inputs and show each request without sending it118- Partial run: execute only selected steps or start from a selected step1191201. Read the flow file and parse each step in order.1212. Build a session variable map for captured values and external input values.1223. Resolve placeholders in path, headers, and body before each request.1234. Decide the execution mode from the user request. If the user asks to preview, validate, inspect, or verify without side effects, use dry run.1245. If the user asks to run only part of the flow, determine either:125 - a single target step126 - a start step and continue to the end127 - explicit steps to skip1286. For partial runs, make sure required inputs for skipped earlier steps still exist. Prefer existing env values or ask the user before running if a skipped step would have produced required captures.1297. Execute requests sequentially when not in dry run. Use `curl -sS` as the canonical request transport. Use `jq` for JSON extraction. If `jq` is unavailable or the extraction is too complex, use `python3` only for parsing or extraction, not for sending requests.1308. Validate expectations after each response.1319. Stop on the first unexpected failure unless the user explicitly asks to continue.132133Environment resolution rules:1341351. Determine the flow identifier from the directory name when the flow path is `<flow-name>/flow.md`. Use kebab-case such as `customer-login`.1362. Detect available per-flow environment files such as `./.env.local`, `./.env.staging`, and `./.env.production`.1373. If exactly one per-flow environment file exists, use it and state which environment was selected.1384. If multiple per-flow environment files exist, ask the user which environment to use before executing the flow.1395. If no per-flow environment file exists, fall back to repo-wide environment-specific secret files.1406. If multiple repo-wide candidates exist, ask the user which one to use.141142Environment selection SOP:1431441. When multiple environment files exist for the same flow, ask the user explicitly which one to use.1452. Use a direct prompt such as: `Flow "customer-login" has environments: local, staging, production. Which one should I run?`1463. Do not guess the environment from the current branch, hostname, or prior conversation unless the user already specified it in the current request.1474. If `production` is chosen, ask for explicit confirmation again before sending any request with side effects.148149For each step, report:150151- Step name152- Request summary: method and path153- Execution mode when not doing a normal full run154- Response status155- Key assertion result156- Captured variable names157158Do not print secrets in full. Redact bearer tokens, refresh tokens, API keys, and cookies in the summary.159160In dry run mode, report:161162- resolved method and path163- resolved headers with secrets redacted164- resolved body with secrets redacted165- resolved `curl` command166- whether every placeholder was resolved167- whether the step is ready to execute168169Prefer a single generated `curl` command per step for replay and debugging. Do not generate ad-hoc Python scripts to send HTTP requests unless `curl` cannot represent the request accurately.170171## Capturing Variables172173When a step defines captured variables:1741751. Parse the response body as JSON.1762. Extract each value from the declared JSON path.1773. Store successful captures in the session variable map.1784. Fail fast if a required capture is missing, and show the relevant response snippet plus the missing path.179180If a response body is not JSON, say so explicitly and do not pretend extraction succeeded.181182## Assertions And Negative Tests183184Treat non-2xx responses as valid only when the flow explicitly expects them.185186Rules:1871881. If `Status` is `401`, `403`, `404`, `409`, `422`, or another non-2xx value, treat the step as a negative test step.1892. For negative test steps, still validate all declared expectations.1903. If the flow expects a non-2xx status and the server returns `2xx`, mark the step as failed.1914. If the flow expects `2xx` and the server returns non-2xx, mark the step as failed.1925. When negative-test intent is not clear, ask the user before rewriting the flow.193194Example expectation block:195196```markdown197**Expect:**198- Status: 422199- JSON path exists: `.error.code`200- JSON path equals: `.error.code` -> `INVALID_PHONE_NUMBER`201- JSON path contains: `.error.message` -> `phone`202```203204## Partial Run Rules205206When the user asks to run only part of the flow:2072081. Accept requests such as `run step 3`, `start from step 2`, `skip login`, or `only run get profile`.2092. Resolve step references by step number first, then by exact step name.2103. If a later step depends on captured values from skipped steps, prefer env-backed placeholders or ask the user how to supply them.2114. Do not silently invent missing captured values.2125. Report clearly which steps were skipped and why.213214## Creating or Updating a Flow2152161. Inspect the current API reference and any related existing flows.2172. Draft the flow using the documented format from the example reference.2183. Keep the flow definition environment-agnostic when the same flow will run against multiple targets.2194. If the new flow uses secret-backed or environment-backed placeholders, create a sibling `.env.example` file in the same flow directory and include every required placeholder-backed key with blank or example-safe values only.2205. If the flow will rely on `.env`, `.env.*`, or per-flow secret files, check whether `.gitignore` already ignores those real secret files before writing or recommending them.2216. If the ignore rule is missing, ask before editing a tracked `.gitignore`, then add the necessary entries so real secret files stay untracked while `.env.example` remains committable.2227. Keep `.env.example` safe to commit by using blank values or obviously non-secret placeholders only.2238. If secret-file keys change, update `.env.example` in the same change so it remains an accurate contract for required inputs.2249. If the user asked for an edit, summarize the intended changes before writing.22510. If the flow needs credentials, phone numbers, OTPs, or other sensitive values, keep them as placeholders and define how they will be provided at runtime through `.env.<environment>` files.22611. Ask for approval before saving any new or modified flow file.22712. After approval, write the file and optionally run it.228229Never invent request or response fields when the spec or code does not support them. Mark assumptions clearly.230231## When to Ask the User232233Ask before proceeding when:234235- The base URL is unclear and multiple plausible targets exist236- The OpenAPI spec is missing locally and probing server routes could hit the wrong service237- Multiple `.env.<environment>` files exist for the same flow and the user has not chosen one yet238- The flow may target `production` or another live environment with real side effects239- A flow depends on credentials, phone numbers, OTP codes, or other sensitive values that are not already available from a safe local source240- A partial run skips earlier steps that would normally generate required captured values241- The repo does not yet have an ignored file for local flow secrets and creating one would change tracked files such as `.gitignore`242- A non-2xx step could be either an expected negative test or an actual failure243244Do not ask unnecessary implementation-detail questions when the repo or spec already answers them.245246## Failure Handling247248- Server unreachable: report the base URL and the connection failure clearly.249- Unexpected non-2xx status: show the step, status, and a short response excerpt.250- Missing variable capture: stop and show which JSON path failed.251- Spec missing: continue if the flow is otherwise clear, but say validation is limited.252- Placeholder unresolved: stop before sending the request and show the missing variable name.253- Production target: require explicit user confirmation before executing any write action or any endpoint with business side effects.254255## Output Style256257Keep the result compact and structured. A good run summary looks like:258259```text260Step 1: Login261- Request: POST /auth/login262- Response: 200263- Captured: access_token, user_id264265Step 2: Get profile266- Request: GET /users/42267- Response: 200268- Assertions: email exists269```270271For dry run or partial run, include a short preface such as:272273```text274Mode: dry run275Selected steps: 2-3276Secret source: tests/flows/customer-login/.env.staging277```278279## Recommended Conventions280281- Committed flow files: keep under `tests/flows/<flow-name>/flow.md` unless the repo already uses another convention282- Use `kebab-case` for flow directory names, for example `customer-login`, `admin-refresh-session`, `booking-create-and-pay`283- Each committed flow should declare `Base URL` explicitly, usually as `{{BASE_URL}}`284- Add `OpenAPI URL` to the flow whenever the spec is served over HTTP or differs by environment, usually as `{{OPENAPI_URL}}`285- Do not hardcode `Environment` into `flow.md` when the same flow can run against multiple targets286- Do not hardcode `Secrets File` into `flow.md` unless the user explicitly wants an override287- Per-flow secret files should be the default: `tests/flows/<flow-name>/.env.local`, `tests/flows/<flow-name>/.env.staging`, `tests/flows/<flow-name>/.env.production`288- Commit a per-flow example file when useful: `tests/flows/<flow-name>/.env.example`289- Repo-wide secret files are fallback only: `.flow.test.local.env`, `.flow.test.staging.env`, `.flow.test.production.env`290- Example secret files: prefer committed `*.env.example` files with blank or placeholder values only291- Placeholder names: use uppercase snake case such as `{{ADMIN_EMAIL}}`, `{{TEST_PHONE_NUMBER}}`, `{{OTP_CODE}}`