Endpoint
Use case: Content - Overwrites content it controls.
Put an endpoint: directive above a curl snippet and Raindown writes the response in a code block below it, typed by its content type, after a comment of its status, how long it took and the date it was fetched:
endpoint:
```sh
curl https://httpbin.org/uuid
```
<!-- response 200 · 142 ms · 2026-10-14 -->
```json
{
"uuid": "4c9d6a2e-…"
}
```
Bare, in a Raindown file (.rd), a Markdown file with raindown: true front matter, or with --bare, it ends at the next blank line, like other bare blocks.
Running requests
Endpoints only run when you pass -x (or --execute), and then only the ones whose response block is missing or empty. Without it, Raindown never sends a request, so opening a file is safe:
raindown api.md -x
raindown watch api.md -x
In the terminal UI, select an endpoint and press x, or click its [Run] button, to run just that one, replacing its response.
Refreshing responses
To refresh a response, empty its code block and run Raindown with -x, and its fetched date, status and time are updated too.
They're in an HTML comment, so they're hidden on the page, but anyone reading the file or the HTML source, people or agents, can tell how fresh the example is and whether it worked.
Variables
Like Postman or Bruno, a snippet can use variables, like {base_url} and {token}. Values come from, in order, with later ones winning:
- The file's front matter.
- The project's environment.
- Earlier responses, named with
as:.
{env.NAME} is the environment variable NAME, so secrets stay out of your files:
curl -H 'Authorization: Bearer {env.API_TOKEN}' {base_url}/me
Placeholders without a value, like the braces in a JSON body, are left as they are. Each word of the snippet is filled in after it's split, so a value can't add curl options.
Environments
Environments go in .raindown/environments.yml, and an environment's values can read environment variables too:
local:
base_url: http://localhost:3000
staging:
base_url: https://staging.example.com
token: '{env.STAGING_TOKEN}'
The first environment is used unless you pass --env or set RAINDOWN_ENV:
raindown api.md -x --env staging
RAINDOWN_ENV=staging raindown watch -x
Secrets
Put secrets in .raindown/environments.local.yml, which adds to and overrides environments.yml, environment by environment:
staging:
token: sk-live-…
raindown init creates a .raindown/.gitignore that leaves it out of git. If your project's .raindown folder came before that, add environments.local.yml to a .gitignore yourself.
Values from environments.local.yml and {env.NAME} are secret: when a response or an error echoes one, Raindown writes its placeholder, like {token}, instead of the value. A later request that reads that response, like {me.token}, still gets the real value. Values shorter than four characters aren't hidden, as they'd match anywhere in a response.
Chaining requests
Name an endpoint with as: and later snippets in the file can read its response. JSON is read by path, like {login.token} or {users.0.id}, and any response as a whole, like {login}:
endpoint: as: login
```sh
curl -d 'user=ada' {base_url}/login
```
endpoint:
```sh
curl -H 'Authorization: Bearer {login.token}' {base_url}/me
```
With -x, endpoints run from the top of the file down, so a login runs before the requests that use its token. Responses are read from the file, so running one endpoint on its own, like from the terminal UI, still uses the responses above it.
Checking responses
Like tests in Postman or Bruno, an endpoint can say what its response should be, with status: and schema::
endpoint: as: user status: 200 schema: Schema[id: Integer, email: String | nil, tags: Array[String]]
```sh
curl {base_url}/users/1
```
A schema is a LowType type. Schema[] is a hash with named keys, a key whose type allows nil may be missing, and keys it doesn't name are allowed. Nest them, like Schema[address: Schema[city: String]], and check lists, like Array[Schema[id: Integer]]. Write schemas with Schema[], not {}, which reads as a variable. Raindown reads the type without running it, so only types, values, | and [] on Array, Hash, Schema, Enum and Status are allowed.
Named schemas
For a bigger schema, name it with schema: and its fields below it, indented, then give its name to the endpoints below it:
<!-- schema: User
id: Integer
email: String | nil
address:
city: String
-->
endpoint: schema: User
See Schema directive.
Running files you trust
Warning
Be careful which files and folders you run Raindown on with -x. It runs every endpoint in the files it opens or watches, including files added to a watched folder later, so only use it on files you've read or trust, as you would before running make in a repo.
Requests go out from your machine, so a snippet can reach anything it can, like servers running on it at localhost and services on your private network. With variables, it can also send your environment variables, like API keys, wherever its URL points.
Raindown guards against the worst of it:
- Files can't run themselves. Only
-x, or running an endpoint in the terminal UI, sends requests. Reading a file, rendering it without-x, and the VS Code preview never do. - Cloud metadata is refused. Link-local addresses (
169.254.0.0/16andfe80::/10), like the cloud metadata service at169.254.169.254that can give out a server's credentials, are refused however they're written, like2852039166or[::ffff:169.254.169.254]. - Hosts are checked before they're requested. Raindown looks up each host itself and pins curl to the address it checked, with
--resolve, so a name can't point somewhere else by the time curl connects. - Redirects aren't followed.
-Lis rejected, so a public URL can't redirect to an address that would be refused. - Secrets aren't written back. A response or error that echoes a secret is written with its placeholder.
Behind a proxy, set with https_proxy or ALL_PROXY, the proxy looks names up rather than curl, so Raindown only checks hosts written as IPs and leaves names to the proxy.
Limits
The snippet runs with the curl binary but not through a shell, so pipes, redirects and $VARIABLES aren't supported, but variables are.
Curl is limited to HTTP requests, values can't read files (-d @body.json), and options like -o, -F, -K or -L are rejected. Raindown also runs curl without your ~/.curlrc.