Files
yaak-mountain-loop/crates-cli/yaak-cli/skills/use-yaak/references/requests.md
T
Gregory Schier 314f08545c Add a Yaak CLI skill for coding agents, installed by yaak agent install
Teaches agents to drive the CLI: workspaces, environments, requests,
sending, response chaining, and importing. SKILL.md stays lean with
four references loaded on demand.

The skill is embedded in the binary and written to ~/.agents/skills plus
any detected tool directory, so it ships with the CLI and refreshes on
update. Reinstalls keep files edited locally unless --force.

Also fixes the `request schema http` hint for URL path parameters, which
said to omit the leading colon. Names without the colon are sent as query
string parameters instead, leaving the placeholder literal in the path.
2026-08-13 19:26:44 -07:00

196 lines
7.2 KiB
Markdown

# Authoring requests
Everything here is a field on the HTTP request model. Run
`yaak request schema http --pretty` to see the full, current schema, including
the authentication variants contributed by installed plugins.
## Two ways to create
Name, method, and URL have flags. Everything else needs JSON:
```bash
yaak request create wk_abc123 --name "List Pets" --method GET --url "https://api.example.com/pets"
```
```bash
yaak request create wk_abc123 --json '{
"name": "Create Pet",
"method": "POST",
"url": "https://api.example.com/pets",
"bodyType": "application/json",
"body": {"text": "{\"name\":\"Rex\",\"species\":\"dog\"}"},
"headers": [{"name": "Content-Type", "value": "application/json", "enabled": true}]
}'
```
Flags and JSON cannot be mixed on the same command. The workspace ID may be
omitted when only one workspace exists, and may also be carried inside the
payload as `workspaceId`.
## Bodies
`bodyType` decides how `body` is encoded onto the wire, and `body` is a
free-form object whose shape depends on that type. For text-ish types the
content lives in `body.text` as a **string**, so a JSON payload is
double-encoded: a JSON string containing JSON.
| `bodyType` | `body` |
|---|---|
| `application/json` | `{"text": "{\"key\":\"value\"}"}` |
| `text/xml` | `{"text": "<root/>"}` |
| `other` | `{"text": "…"}` |
| `graphql` | `{"query": "{ pets { id } }", "variables": "{\"n\":1}", "operationName": ""}` |
| `application/x-www-form-urlencoded` | `{"form": [{"name":"a","value":"1","enabled":true}]}` |
| `multipart/form-data` | `{"form": [{"name":"file","file":"/abs/path","enabled":true}]}` |
| `binary` | `{"filePath": "/abs/path"}` |
| `null` (omitted) | no body |
For `graphql`, note that `variables` is a **string** of JSON, not an object, and
that a GraphQL request sent with method `GET` moves query/variables/operationName
into the query string and sends no body at all.
For `multipart/form-data`, each entry is either a text field (`value`) or a file
(`file`, an absolute path), and may carry its own `contentType`.
**Add the `Content-Type` header yourself.** In the desktop app, choosing a body
type also writes a matching `Content-Type` into the request's headers, so it is
stored on the request rather than inferred at send time. Creating a request from
the CLI skips that step: `bodyType` alone controls how the body is *encoded*, and
nothing adds the header. A JSON body with no `Content-Type` goes out as untyped
bytes, which many APIs answer with 400 or 415.
```json
"bodyType": "application/json",
"body": {"text": "{\"name\":\"Rex\"}"},
"headers": [{"name": "Content-Type", "value": "application/json", "enabled": true}]
```
Use the same value as `bodyType`, with two exceptions the app also makes: `other`
pairs with `text/plain`, and `graphql` pairs with `application/json`. Multipart is
the one case to leave alone — the sender replaces that header with one carrying
the generated boundary.
Requests created this way end up identical to app-created ones, which matters
because the user will open them in the app afterwards.
## Headers
```json
"headers": [
{"name": "Accept", "value": "application/json", "enabled": true},
{"name": "X-Debug", "value": "1", "enabled": false}
]
```
`enabled: false` keeps a header in the app for the user to toggle without
sending it. Values accept template variables.
## URL parameters
One array covers both query string entries and path placeholders. A parameter
fills a path placeholder only when its **name starts with a colon** and matches
the placeholder in the URL. Everything else becomes a query string entry:
```json
"url": "https://api.example.com/pets/:petId/visits",
"urlParameters": [
{"name": ":petId", "value": "42", "enabled": true},
{"name": "limit", "value": "10", "enabled": true}
]
```
That sends `https://api.example.com/pets/42/visits?limit=10``:petId` is
substituted into the path and dropped from the query string, `limit` is not.
This is the single easiest thing to get wrong here, and it fails **silently**.
Naming the parameter `petId` instead of `:petId` leaves `/pets/:petId/visits` in
the path as literal text and appends `?petId=42`, which most servers answer with
a 404. Always include the colon, and confirm with `yaak -v request send <id>`
that the `> GET …` line shows a substituted path.
## Authentication
`authenticationType` names a strategy and `authentication` holds its values. The
strategy names are not always what you would guess — the built-ins are `basic`,
`bearer`, `apikey`, `jwt`, `oauth1`, `oauth2`, `awsv4` (not "aws"), and
`windows` (not "ntlm"). Installed plugins can add more.
```json
"authenticationType": "bearer",
"authentication": {"token": "${[ api_token ]}"}
```
```json
"authenticationType": "basic",
"authentication": {"username": "admin", "password": "${[ admin_password ]}"}
```
```json
"authenticationType": "apikey",
"authentication": {"location": "header", "key": "X-Api-Key", "value": "${[ api_key ]}"}
```
The exact fields for each strategy, and which are required, are in the
`authentication` property of `yaak request schema http --pretty`, which
enumerates every installed strategy as a named variant. Read it rather than
guessing — `oauth2` has fifteen fields and `jwt` requires four.
Set `authenticationType` to `null` to send no auth and stop inheriting from the
parent folder.
## Folders and inheritance
Folders are containers *and* a place to put shared configuration. Headers and
authentication set on a folder apply to every request inside it, so the common
pattern is one folder per API surface holding the auth:
```bash
yaak folder create wk_abc123 --name "Admin API"
yaak folder update --json '{
"id": "fl_abc123",
"authenticationType": "bearer",
"authentication": {"token": "${[ admin_token ]}"},
"headers": [{"name": "X-Api-Version", "value": "2024-01-01", "enabled": true}]
}'
yaak request create wk_abc123 --json '{"name":"List Users","method":"GET","url":"${[ base_url ]}/users","folderId":"fl_abc123"}'
```
The request above sends both the folder's bearer token and its version header
without repeating either. A request that sets its own `authenticationType`
overrides the folder's.
Nest folders by setting a folder's `folderId`. `yaak send <fl_id>` sends every
request in the folder recursively.
## Per-request settings
Each `setting*` field is an inherited toggle shaped
`{"enabled": bool, "value": …}`, where `enabled` means "override the inherited
value" rather than "turn the feature on":
```json
"settingFollowRedirects": {"enabled": true, "value": false},
"settingRequestTimeout": {"enabled": true, "value": 5000}
```
Available: `settingFollowRedirects`, `settingRequestTimeout` (ms, `0` for none),
`settingValidateCertificates`, `settingSendCookies`, `settingStoreCookies`.
## Updating
Updates are JSON merge patches keyed by `id`. Send only what changes:
```bash
yaak request update --json '{"id":"rq_abc123","method":"PATCH"}'
```
Arrays are replaced wholesale, not merged — to add one header, read the current
list with `yaak request show rq_abc123` and write the full new array back.
Setting a key to `null` removes it.
## Ordering
`sortPriority` (a float) controls display order in the app sidebar. Leave it at
`0` unless the user cares; requests created with the same priority fall back to
creation order.