A simple, keyboard-driven TUI for testing HTTP endpoints.
- File-based request management (
.httpand.yamlfiles) - Keyboard-driven navigation
- Header profiles for quick account switching
- Variable substitution and support of simple and multi-values fields
- Shell command variables - Execute shell commands in variables with
$(command)syntax - Quick file duplication and in-app file renaming
- Profile creation in TUI - Create new profiles without editing JSON
- Auto-save session state
- Request/response history with timestamps
- OAuth 2.0 authentication flow (PKCE support)
- OpenAPI/Swagger import - auto-generate .http files
- Interactive documentation viewer with collapsible fields
- Global config directory (
~/.restcli/) - Compiled binaries
- YAML format support with JSON schema for autocomplete
- JSON response beautification
- Response scrolling
- CLI mode with structured output - JSON/YAML formats for scripting
- Stdin body override - Pipe request bodies from other commands
- Smart TTY detection - Auto-formats output for terminals vs pipes
- Fullscreen mode for focused response viewing
# Build the binary
deno task build
# Run it (auto-initializes ~/.restcli/ on first run)
./restcli
# Or install globally
sudo mv restcli /usr/local/bin/
restcli # Use from anywhere!# Run the TUI
deno task dev
# Run a specific request file
deno task run requests/example.http
# Convert cURL to .http (from browser DevTools, docs, etc.)
pbpaste | restcli-curl2http --output requests/my-request.http
# Or with deno:
pbpaste | deno task curl2http --output requests/my-request.httpSee INSTALL.md for detailed installation and setup guide.
See PROFILES.md for detailed profile configuration guide.
See CURL2HTTP.md for converting cURL commands to .http files.
See OPENAPI.md for converting openapi to .http files.
See DOCUMENTATION.md for adding documentation to your requests.
restcli can run in two modes:
Run without any arguments to open the interactive TUI:
# Opens the TUI
restcliRun requests directly from the command line:
# Basic usage - outputs only response body (perfect for piping)
restcli requests/example.http
# Pipe to jq for JSON processing
restcli requests/users/list.http | jq '.users[] | .name'
# Full output mode - shows status, headers, and body
restcli --full requests/example.http
restcli -f requests/example.http
# Structured JSON output (includes status, headers, body, duration, sizes)
restcli --output json requests/example.http | jq '.status'
restcli -o json requests/example.http
# YAML structured output
restcli --output yaml requests/example.http
restcli -o yaml requests/example.http
# Save response to file (auto-detects format from extension)
restcli --save response.json requests/example.http # JSON format
restcli --save response.yaml requests/example.http # YAML format
restcli --save response.txt requests/example.http # Text format
# Or explicitly specify format (overrides extension)
restcli -s output.txt -o json requests/example.http
# Override request body from command line
restcli --body '{"key":"value"}' requests/example.http
restcli -b '{"user":"admin"}' requests/example.http
# Pipe body from another command
echo '{"dynamic":"data"}' | restcli requests/example.http
cat payload.json | restcli requests/create.http
# Use with profile
restcli --profile Admin requests/example.http
restcli -p Admin --yaml requests/example.http
# Show help
restcli --help
restcli -hCLI Flags:
--help,-h: Show help message--full,-f: Show full output (status line, headers, and body)--body-only: Show only response body (explicit override)--output <format>,-o <format>: Output format:json,yaml, ortext(default:text)json: Structured output with status, headers, body, duration, and sizesyaml: Same structured data in YAML formattext: Body only (current behavior)
--save <file>,-s <file>: Save response to file instead of stdout--body <json>,-b <json>: Override request body with inline JSON--yaml,-y: Convert JSON response to YAML format (deprecated, use-o yaml)--profile <name>,-p <name>: Use a specific profile for the request
Smart Output Detection:
The CLI automatically detects whether output is to a terminal (TTY) or being piped:
- When piped (e.g.,
restcli file.http | jq): Body-only output (perfect for JSON processing) - In terminal: Full output with status and headers by default
- Override with
--fullor--body-onlyflags
Output Format Priority:
When multiple output format sources are present:
--outputflag (highest priority)- File extension in
--saveflag (.json,.yaml,.txt) - Profile's
outputsetting - Default (
text)
Body Override Priority:
When multiple body sources are present:
--bodyflag (highest priority)- Stdin (if piped)
- Request file body (default)
Profile Output Configuration:
Set a default output format in your profile's .profiles.json:
{
"name": "My Profile",
"output": "json",
"headers": {},
"variables": {}
}This affects:
- CLI mode: Default format when no
--outputflag is used - TUI
sshortcut: Format used when saving responses withskey - Supported values:
"json","yaml","text"(default)
### Request Name (optional)
METHOD url
Header: value
Another-Header: value
{
"body": "for POST/PUT"
}
###
### Login
POST {{baseUrl}}/auth/login
Content-Type: application/json
{
"username": "test",
"password": "pass"
}
### Get Profile
GET {{baseUrl}}/users/{{userId}}
Authorization: Bearer {{token}}
Variables use {{varName}} syntax in your requests. Headers and variables are configured in profiles (.profiles.json).
Variables can execute shell commands using $(command) syntax:
{
"name": "Dynamic Profile",
"variables": {
"timestamp": "$(date +%s)",
"gitBranch": "$(git branch --show-current)",
"randomId": "$(uuidgen)",
"apiKey": "$(cat ~/.secrets/api-key)"
}
}Features:
- Commands execute when variables are resolved (before each request)
- 5-second timeout per command
- Works on Linux, macOS (uses
sh) - Supports any shell command that outputs to stdout
- Errors are logged and result in empty string
Security Note: Shell commands run with your user permissions. Only use trusted commands in your profiles.
Profiles store your headers and variables permanently. Create profiles in .profiles.json:
[
{
"name": "User 1",
"headers": {
"Authorization": "Bearer {{token}}",
"X-User-ID": "user1"
},
"variables": {
"baseUrl": "http://localhost:3000",
"token": "user1-token-here"
},
"workdir": "",
"editor": "zed",
"output": "json",
"oauth": { }
},
{
"name": "User 2",
"headers": {
"Authorization": "Bearer {{token}}",
"X-User-ID": "user2"
},
"variables": {
"baseUrl": "http://localhost:3000",
"token": "user2-token-here"
},
"workdir": "",
"editor": "zed",
"output": "text",
"oauth": { }
}
]Press p in the TUI to cycle through profiles.
.session.json contains ephemeral state that is linked to the currently active profile:
- Active profile name
- Temporary runtime variables (auto-extracted tokens, etc.)
- Session state gets cleared when you switch profiles
Important: Configure your headers and variables in .profiles.json, not .session.json.
The TUI auto-extracts token or accessToken from JSON responses and temporarily stores them in the session.
↑/↓- Navigate files (circular)Page Up/Down- Fast scroll (jumps by visible page size):- Goto line in hexCtrl+r- Search files by name (pressCtrl+ragain to cycle through matches)
i- Inspect request (preview what will be sent without executing)Enter- Execute requestx- Open file in external editor (configured in profile)X(Shift+X) - Configure external editor for active profiled- Duplicate current fileR(Shift+R) - Rename current files- Save response/inspection to file (timestamp-based filename)c- Copy response body/error to clipboardr- Refresh file list and reload profiles/sessionn- Create new profile interactivelyp- Switch profile (cycles through profiles)v- Open variable editor (add, edit, delete variables)h- Open header editor (add, edit, delete headers)P(Shift+P) - Open .profiles.json in external editorS(Shift+S) - Open .session.json in external editorm- View request documentationj- Scroll response downk- Scroll response upb- Toggle response body visibilityf- Toggle fullscreen mode (hide sidebar)H(Shift+H) - View request history
o- Start OAuth authentication flow (uses profile's OAuth config)O(Shift+O) - Configure OAuth settings for current profile
?- Show helpESC- Clear status message / Cancel search or gotoq- Quit
Press v to open the interactive variable editor. This allows you to manage profile variables:
- Navigate variables with
↑/↓ a- Add new variableeorEnter- Edit selected variable's valued- Delete selected variableO(Shift+O) - Open options selector (for multi-value variables)M(Shift+M) - Manage options (add/edit/delete options for multi-value variables)ESC- Exit variable editor
- Type to enter key name
Tab- Move to value fieldShift+Tab- Go back to key field (to fix typos)- For Simple variables:
- Type value and press
Enterto save
- Type value and press
- For Multi-value variables:
- Press
Tabagain to toggle to Multi-value type - Enter each option and press
Enter(field clears, ready for next) - Press
1-9to set which option is active (marked with ✓) - Press
Enteron empty field to finish and save - Press
Tabto toggle back to Simple (confirms if options exist)
- Press
Ctrl+k- Clear current fieldESC- Cancel and return to list
- Type to change the value (key cannot be changed)
Enter- Save changes to active profileESC- Cancel
y- Confirm deletion from profilenorESC- Cancel
Variables can have multiple predefined options with one active value. This is useful for switching between environments, API versions, or any value with a fixed set of choices.
Creating Multi-Value Variables:
Edit .profiles.json manually to create multi-value variables:
{
"name": "My Profile",
"variables": {
"baseUrl": "https://api.example.com", // Simple variable
"environment": {
// Multi-value variable
"options": ["dev", "staging", "prod"],
"active": 2,
"description": "API environment"
},
"apiVersion": {
"options": ["v1", "v2", "v3"],
"active": 0
}
}
}Using Multi-Value Variables in TUI:
Multi-value variables are displayed with a [N options] ◀ indicator showing the currently active value:
Quick Option Selection (Press O (Shift+O)):
- Navigate with
↑/↓arrow keys - Press
1-9for instant selection (first 9 options) - Press
Enterto select highlighted option - Current active option marked with
✓ - Press
ESCto cancel
Manage Options (Press M (Shift+M)):
a- Add new option to the liste- Edit/rename selected optiond- Delete option (cannot delete active option)Space- Set selected option as active↑/↓- Navigate optionsESC- Return to variable list
Press X (Shift+X) to configure an external editor for the active profile, then press x to open the selected file in that editor.
- Press
X (Shift+X)to open the editor configuration modal - Enter your editor command (e.g.,
zed,code,vim,nvim,subl,etc.) - Press
Enterto save
The editor setting is saved per-profile in .profiles.json:
{
"name": "My Profile",
"workdir": "",
"editor": "zed",
"headers": { },
"variables": { },
"oauth": { }
}Once configured, press x on any request file to open it in your editor. The editor opens in the background, so the TUI remains running.
Press h to open the interactive header editor. This allows you to manage profile headers.
The header editor works identically to the variable editor:
- Navigate headers with
↑/↓ a- Add new headereorEnter- Edit selected header's valued- Delete selected headerESC- Exit header editor
- Type to enter header name and value
Tab- Switch between name and value fieldsEnter- Save header to active profileESC- Cancel
- Type to change the value (header name cannot be changed)
Enter- Save changes to active profileESC- Cancel
y- Confirm deletion from profilenorESC- Cancel
Press O (Shift+O) to configure OAuth 2.0 settings for the active profile. The OAuth configuration supports two modes:
Provide a complete authorization endpoint URL directly:
authEndpoint: Full authorization URL with all parameterstokenUrl: Token endpoint for code exchange (required for authorization code flow)responseType:code(default) ortoken
Build the authorization URL from components:
authUrl: Base authorization URLtokenUrl: Token endpoint URLclientId: OAuth client IDclientSecret: OAuth client secret (optional)redirectUri: Callback URL (default:http://localhost:8888/callback)scope: OAuth scopes (default:openid)responseType:code(default) ortokenwebhookPort: Local server port (default: 8888)tokenStorageKey: Variable name to store token (default:token)
Press o to start the OAuth authentication flow:
- Local webhook server starts on configured port
- Browser opens to OAuth provider
- User completes authentication
- OAuth provider redirects back to local server
- Authorization code is exchanged for access token
- Token is automatically stored in profile variables
The flow supports PKCE (Proof Key for Code Exchange) for enhanced security.
Press H (Shift+H) to view request history:
- See all previously executed requests with timestamps
- Navigate with
↑/↓arrow keys - Press
Enterto view full request/response details - History is stored in
~/.restcli/.history.json - Each entry includes: timestamp, file path, method, URL, status, and response time
Press m to view interactive documentation for the current request:
- Shows request parameters, examples, and response schemas
- Navigate with arrow keys
- Press
Spaceto expand/collapse nested fields - Useful for understanding API endpoints with complex request/response structures
- Use duplicate (
d) to quickly create variations of requests - Profile headers are merged with request headers (request headers take precedence)
- Files are auto-discovered from
./requests/directory - Text selection: Use
sto save orcto copy response. - Use
rto refresh file list after creating new.httpfiles outside the TUI - Inspect before executing: Press
ito preview the final request:- See the actual URL after variable substitution (
{{baseUrl}}→http://localhost:3000) - View all headers including those from active profile
- Check the request body before sending
- Useful for debugging variable issues or verifying profile headers
- See the actual URL after variable substitution (
- Quick navigation:
- Files are numbered in hexadecimal (shown in sidebar)
- Use
:followed by hex number (e.g.,:64to jump to file #100) - Use
Ctrl+rto search by filename, thenCtrl+ragain to cycle through matches - Search is case-insensitive and matches anywhere in the filename
- Arrow keys wrap around (circular)
- Page Up/Down for fast scrolling through long lists (jumps by ~1 screen height)