Parley 🏴☠️
parley

A git-friendly TUI HTTP client. Collections are plain TOML files — diff them, commit them, review them.
Why I built Parley
Most HTTP clients are great for sending requests, but not great for collaboration. In many teams, request collections end up buried in proprietary formats or huge JSON exports that are hard to review in pull requests.
Parley is my attempt to solve that friction with a simple rule: collections should be text-first.
Every collection is a plain .toml file, easy to read, version, diff, and discuss in Git. No hidden state, no binary blobs, and no lock-in around how request definitions are stored.
Repository: github.com/gianlucarea/parley
Project overview
Parley is a terminal-first HTTP client written in Go, designed around developer workflows that already live in the shell and in Git.
At a high level, the app does four things:
- →Loads request collections from TOML files.
- →Resolves variables from
.parley.envand shell environment values. - →Executes HTTP requests with auth support.
- →Renders everything inside a three-pane TUI for fast iteration.
Architecture and components
Collection parser (internal/collection)
- →Loads
.tomlcollection files from a file path or an entire directory - →Supports multiple requests per file, each with a name, method, URL, headers, body, and auth
- →Collection name is derived from the filename (no extension)
Environment & variable substitution (internal/env)
- →Parses a
.parley.envfile (key=value, optional quotes,#comments) - →Replaces
{{VAR}}placeholders in URLs, headers, bodies, and auth fields - →Substitution precedence: shell environment >
.parley.env> literal value
HTTP executor (internal/http)
- →Executes
GET,POST,PUT,PATCH,DELETErequests - →Injects
Authorization: Bearer <token>forbearerauth - →Injects
Authorization: Basic <b64>forbasicauth - →Auth field values also support
{{VAR}}substitution - →Returns status code, status text, response headers, body, and elapsed time
TUI (internal/tui)
Built with Bubbletea, Bubbles, and Lipgloss.
The interface uses a three-pane layout:
| Pane | Description |
|---|---|
| Sidebar | Lists all collections and their requests. Method badges are color-coded (GET green, POST yellow, PUT blue, PATCH cyan, DELETE red). Built-in filter with /. |
| Editor | Shows the selected request's method, URL, headers, body, and auth. Press Enter to execute. |
| Viewer | Displays the response: status code, elapsed time, and a syntax-highlighted scrollable body (powered by Chroma). |
Keyboard shortcuts
| Key | Action |
|---|---|
Tab | Cycle focus between panes |
↑ / ↓ | Navigate requests in sidebar |
Enter | Select request (sidebar) / Run request (editor) |
/ | Filter requests |
Esc | Clear filter |
? | Toggle help overlay |
q / Ctrl+C | Quit |
Quickstart (from source)
# 1. Build from source
git clone https://github.com/gianlucarea/parley
cd parley
go build -o parley ./cmd/parley
# 2. Create a collection file
cat > api.toml << 'EOF'
[[requests]]
name = "List Users"
method = "GET"
url = "{{BASE_URL}}/users"
[requests.headers]
Accept = "application/json"
[[requests]]
name = "Create User"
method = "POST"
url = "{{BASE_URL}}/users"
body = '{"name":"Alice"}'
[requests.auth]
type = "bearer"
token = "{{API_TOKEN}}"
EOF
# 3. Create a .parley.env (keep this gitignored)
cat > .parley.env << 'EOF'
BASE_URL=https://api.example.com
API_TOKEN=my-secret-token
EOF
# 4. Launch
./parley .
Collection model
Parley uses one .toml file per logical collection. You can place multiple files in the same folder and load them together.
[[requests]]
name = "Get User"
method = "GET"
url = "{{BASE_URL}}/users/{{USER_ID}}"
[requests.headers]
Accept = "application/json"
[requests.auth]
type = "bearer"
token = "{{API_TOKEN}}"
[[requests]]
name = "Create Post"
method = "POST"
url = "{{BASE_URL}}/posts"
body = '{"title":"hello"}'
[requests.auth]
type = "basic"
username = "{{USER}}"
password = "{{PASS}}"
Supported methods: GET POST PUT PATCH DELETE
Auth types:
| type | required fields |
|---|---|
bearer | token |
basic | username, password |
Environment variables and precedence
# Lines starting with # are comments
BASE_URL=https://api.example.com
API_TOKEN=super-secret
USER=alice
PASS=hunter2
Parley resolves variables with this order:
shell environment > .parley.env > literal value
That means you can safely override values per run without touching committed files:
API_TOKEN=other-token ./parley .
What this enables in practice
- →Request definitions become reviewable artifacts in pull requests.
- →Teams can standardize API exploration workflows without coupling to a GUI state file.
- →Local secrets stay local by keeping
.parley.envout of version control.
Future developments
- →Multiple named environments — switch between
dev,staging, andprodprofiles without editing files - →Request history — persist and browse previous responses per request
- →Export to curl — copy the selected request as a ready-to-paste
curlcommand - →Import from OpenAPI / Swagger — generate a collection from an API spec file or URL
- →Response assertions — define expected status codes or body patterns and highlight failures
- →Request chaining — extract values from a response (e.g. a token) and inject them into subsequent requests
- →Pre-built binaries & releases — goreleaser pipeline for Linux / macOS / Windows (amd64 + arm64)
- →WebSocket / SSE support — stream responses in the viewer pane
- →gRPC support — proto-based collections alongside HTTP ones
- →Response diffing — compare two runs of the same request side by side
