Resterm Documentation

June 2, 2026 · View on GitHub

Index

Installation

Prebuilt binaries

  1. Download the archive for your platform from the GitHub Releases page (macOS, Linux, or Windows; amd64 and arm64 builds are published).
  2. Mark the binary as executable (chmod +x resterm on Unix), then copy it into a directory on your PATH.
  3. Launch with resterm --help to confirm the CLI is available.

Build from source

go install github.com/unkn0wn-root/resterm/cmd/resterm@latest

This requires Go 1.24 or newer. The binary will be installed in $(go env GOPATH)/bin.


Quick Start

  1. Place one or more .http or .rest files in a working directory (or use the samples under _examples/).
  2. Run resterm --workspace path/to/project.
  3. Use the navigator sidebar to expand a file ( or Space), highlight a request, and press Ctrl+Enter to send it (Enter runs, Space previews).
  4. Inspect responses in the Pretty, Raw, Headers, Diff, Compare, or History tabs on the right; press g+c to run the current request across the global --compare target list (or its inline @compare directive) and review the results without leaving the editor.

A minimal .http file looks like this:

### Fetch Status
# @name health
GET https://httpbin.org/status/204
User-Agent: resterm
Accept: application/json

### Create Resource
# @name create
POST https://httpbin.org/anything
Content-Type: application/json

{
  "id": "{{$uuid}}",
  "note": "created from Resterm"
}

Initializing a Project

The resterm init command creates a starter set of files in a directory so you can begin working with requests immediately.

mkdir my-api && cd my-api
resterm init

This writes files based on the selected template. Two templates are available:

TemplateFiles created
standard (default)requests.http, resterm.env.json, resterm.env.example.json, rts/helpers.rts, RESTERM.md
minimalrequests.http, resterm.env.json

Both templates add resterm.env.json to .gitignore so secrets stay out of version control. If you prefer to manage .gitignore yourself, pass --no-gitignore.

Flags

FlagDescription
-dir <path>Target directory. You can also pass the path as a positional argument.
-template <name>Template to use (standard or minimal).
-forceOverwrite existing files instead of aborting.
-dry-runPrint actions without writing anything.
-no-gitignoreSkip updating .gitignore.
-listPrint available templates and exit.

Examples

# Create files in a subdirectory
resterm init ./api-tests

# Use the minimal template
resterm init --template minimal

# Preview what would be created
resterm init --dry-run

# Overwrite existing files
resterm init --force

Once the files exist, run resterm in the same directory to open the workspace. Press Ctrl+E to switch between the dev and prod environments defined in resterm.env.json.


UI Tour

Layout

  • Sidebar: unified navigator tree for files, requests, and workflows with a filter bar and tag/method chips. /Space expand files, g+k/g+j expand or collapse the current branch, and g+Shift+K/g+Shift+J expand or collapse all. A detail well beneath the list shows the selected request/workflow summary. When focused, g+h shrinks and g+l expands the sidebar.
  • Editor: middle pane with modal editing (view mode by default, i to insert, Esc to return to view). Inline syntax highlighting marks metadata, headers, and bodies.
  • Response panes: right-hand side displays the most recent response, with optional splits for side-by-side comparisons.
  • Header bar: shows workspace, active environment, current request, and test summaries.
  • Command bar & status: contextual hints, progress animations, and notifications.

Core shortcuts

ActionShortcut
Send active requestCtrl+Enter / Cmd+Enter / Alt+Enter / Ctrl+J / Ctrl+M
Toggle help overlay?
Toggle editor insert modei / Esc
Cycle focus (navigator -> editor -> response)Tab / Shift+Tab
Focus navigator / editor / response panesg+r / g+i / g+p
Open timeline tabCtrl+Alt+L (or g+t)
WebSocket commands (Stream tab)g+w, then i/p/c/l
Adjust sidebar or editor widthg+h / g+l (contextual)
Collapse / expand current navigator branchg+j / g+k
Collapse all / expand all in navigatorg+Shift+J / g+Shift+K
Toggle sidebar / editor / response minimizeg+1 / g+2 / g+3
Zoom focused pane / clear zoomg+z / g+Z
Stack/inline response paneg+s (stack) / g+v (inline)
Jump to top/bottom of focused response tabg+g / G
Cycle Raw tab mode (text / hex / base64, summary for large binary)g+b
Load full Raw dump (hex)g+Shift+D
Save response body / open externallyg+Shift+S / g+Shift+E
Run compare sweep (@compare or --compare targets)g+c
Navigator filter/ to focus; type to search files/requests/tags; Esc clears filter and chips
Navigator: toggle method filter for selected requestm (repeat to switch/clear)
Navigator: toggle tag filters from selected itemt (repeat to toggle)
Navigator: jump to selected request/workflow in editorl / r (when a request or workflow is highlighted)
Open environment selectorCtrl+E
Save fileCtrl+S
Save layout (prompt)g+Shift+L
Open file pickerCtrl+O
Open current/selected file in external editorg+e
New scratch bufferCtrl+T
Reparse current documentCtrl+P (also Ctrl+Alt+P)
Refresh workspace filesCtrl+Shift+O
Split response vertically / horizontallyCtrl+V / Ctrl+U
Pin or unpin response paneCtrl+Shift+V
Choose target pane for next responseCtrl+F or Ctrl+B, then arrow keys or h / l
Show globals summary / clear globals and cookiesCtrl+G / Ctrl+Shift+G (or g Shift+G)
QuitCtrl+Q (or Ctrl+D)

The editor supports familiar Vim motions (h, j, k, l, w, b, gg, G, etc.), insert entries (i, a, I, A, o, O; I moves to the first non-blank character), visual selections with v / V, yank and delete/change operations, undo/redo (u / Ctrl+r), and a search palette (Shift+F or /, toggle regex with Ctrl+R and n moves cursor forward and p backwards).

Custom bindings

Resterm looks for ${RESTERM_CONFIG_DIR}/bindings.toml first and ${RESTERM_CONFIG_DIR}/bindings.json second (default: ~/.config/resterm). Missing files fall back to the built-in bindings. Example:

[bindings]
save_file = ["ctrl+s"]
set_main_split_horizontal = ["g s", "ctrl+alt+s"]
send_request = ["ctrl+enter", "cmd+enter"]
  • Modifiers use + (ctrl+shift+o), while chord steps are separated by spaces ("g s").
  • Bindings can have at most two steps; send_request must remain single-step so it can run inside the editor.
  • Unknown action IDs or duplicate bindings cause the file to be rejected (Resterm logs the error and keeps defaults).

Binding reference

Action IDDescriptionDefault bindings
cycle_focus_nextCycle focus forward (skips editor insert mode).tab
cycle_focus_prevCycle focus backward.shift+tab
open_env_selectorOpen environment picker.ctrl+e
show_globalsShow global variable summary.ctrl+g
clear_globalsClear global variables and cookies.ctrl+shift+g, g shift+g
save_fileSave the current .http / .rest file.ctrl+s
save_layoutPrompt to persist current layout (splits, widths) to settings.g shift+l
toggle_response_split_verticalToggle response inline vs vertical split.ctrl+v
toggle_response_split_horizontalToggle response inline vs horizontal split.ctrl+u
toggle_pane_follow_latestToggle follow-latest for the focused response pane.ctrl+shift+v
toggle_helpOpen/close the help overlay.? (aka shift+/)
open_path_modalOpen the “Open File” modal.ctrl+o
reload_workspaceRescan the workspace root(s).ctrl+shift+o, g shift+o
open_new_file_modalLaunch the “New Request” modal.ctrl+n
open_file_in_editorOpen the current or selected supported file in $RESTERM_EDITOR, $VISUAL, or $EDITOR.g e
open_theme_selectorOpen theme selector.ctrl+alt+t, g m, g shift+t
open_temp_documentOpen a scratch document.ctrl+t
reparse_documentReparse the active buffer.ctrl+p, ctrl+alt+p, ctrl+shift+t
reload_file_from_diskReload the active file from disk (discarding unsaved buffer changes).g shift+r
select_timeline_tabFocus the Timeline tab.ctrl+alt+l, g t
quit_appQuit Resterm.ctrl+q, ctrl+d
send_requestSend the active request (single-step only).ctrl+enter, cmd+enter, alt+enter, ctrl+j, ctrl+m
explain_requestPrepare an Explain preview for the active request without sending it.g x
cancel_runCancel the in-flight request, compare, profile, or workflow run.ctrl+c
copy_response_tabCopy the focused Pretty/Raw/Headers response tab to the clipboard.ctrl+shift+c, g y
Action IDDescriptionDefault bindingsRepeatable
sidebar_width_decrease / sidebar_width_increaseShrink/grow sidebar width (editor split elsewhere).g h, g l
sidebar_height_decrease / sidebar_height_increaseCollapse / expand the selected navigator branch.g j, g k
workflow_height_increase / workflow_height_decreaseCollapse all / expand all navigator branches.g shift+j, g shift+k
focus_requests / focus_response / focus_editor_normalJump directly to a pane.g r, g p, g i
set_main_split_horizontal / set_main_split_verticalStack vs side-by-side editor/response.g s, g v
start_compare_runTrigger compare sweep for the current request.g c
toggle_ws_consoleEnter WebSocket command mode (i console, p ping, c close, l clear).g w
toggle_sidebar_collapse / toggle_editor_collapse / toggle_response_collapseCollapse/expand panes.g 1, g 2, g 3
toggle_zoom / clear_zoomZoom current region / clear zoom.g z, g shift+z

send_request participates in the editor’s “send on Ctrl+Enter” logic, so keep it single-step. All other actions can be remapped to any combination within the constraints above.

Response panes

  • Pretty: formatted JSON (or best-effort formatting for other types).
  • Raw: exact payload text.
  • Stream: live transcript viewer for WebSocket and SSE sessions with bookmarking and console integration.
  • Headers: response and request header subviews with a visible in-pane switcher. Press Enter or Space while focused on the Headers tab to switch between the response headers and the sent request headers (cookies included).
  • Profile / Workflow: latency summaries and histograms from @profile runs plus step-by-step workflow breakdowns. The tab label follows the current run type. Workflow results render as a stable summary plus step list and selected-step detail view. Use j / k or arrow keys to move between steps, Enter to focus the selected step detail, j / k or PageUp / PageDown to scroll that detail, and Esc or Enter to return to the step list.
  • Timeline: per-phase HTTP timings with budget overlays; available whenever tracing is enabled.
  • Diff: compare the focused pane against the other response pane.
  • History: chronological responses for the selected request (live updates). Open a full JSON preview with p or delete the focused entry with d.

When a request opens a stream, the Stream tab becomes available. Use g+w to enter WebSocket command mode, then press i to toggle the console, p to send ping, c to close the socket, or l to clear the live buffer. If the console is actively focused for typing, press Esc first so text entry is not interrupted. Inside the console, use F2 to switch payload modes (text, JSON, base64, file), Ctrl+S or Ctrl+Enter to send frames, and the arrow keys to replay recent payloads.

Use Ctrl+V or Ctrl+U to split the response pane. The secondary pane can be pinned so subsequent calls populate only the primary pane, making comparisons easy.

While the response pane is focused, Ctrl+Shift+C (or g y) copies the entire Pretty, Raw, or Headers tab directly to your clipboard, matching the rendered text (no mouse selection required).

Use g+g and G to jump to the start or end of the Pretty, Raw, or Headers tabs when the response pane is focused. The same keys jump to the first or last entry in the navigator when you are browsing files or workflows.

Binary responses show size and type hints alongside quick previews. For large binary payloads, the Raw tab starts in a summary view and defers full dumps until requested. While the response pane is focused, press g+b to rotate the Raw tab between summary, hex, and base64 views. Press g+Shift+D to load the full hex dump immediately. Press g+Shift+S to open the Save Response Body prompt, which comes prefilled with a suggested path from your last save or workspace and writes the file after you hit Enter. g+Shift+E writes the body to a temporary file and opens it with your default app.

Pane minimization & zoom

  • Toggle the sidebar, editor, or response panes with g+1, g+2, and g+3. Minimized panes collapse into thin frames that display an indicator along with a reminder of the restoring shortcut.
  • Status bar badges (Sidebar:min, Editor:min, Response:min) mirror the current state so you can tell when something is hidden even if the stub scrolls out of view.
  • Use g+z to zoom the currently focused pane and hide the others temporarily; g+Z clears zoom and restores the previous layout (including any manual minimize state).
  • Resize chords such as g+h / g+l are disabled while a related pane is hidden or zoomed, preventing accidental layout resets.

Timeline & tracing

  • Add # @trace directives to enable HTTP tracing on a request. Budgets use phase<=duration notation (dns<=50ms, total<=300ms, etc.) with an optional tolerance= applied to every phase. Supported phases map to nettrace: dns, connect, tls, request_headers, request_body, ttfb, transfer, and total.
  • When a traced response arrives, Resterm evaluates budgets, raises status bar warnings for breaches, and unlocks the Timeline tab. Use Ctrl+Alt+L or the g+t chord to jump straight to it from anywhere.
  • The Timeline view renders proportional bars, annotates overruns, and lists budget breaches. Metadata such as cached DNS results or reused sockets appears beneath each phase, followed by Connection and TLS panels (protocol, reuse, proxy/SSH, resolved IPs, cipher/ALPN, cert chain, SANs, issuer, expiry).
  • Scripts can inspect traces through the trace binding (trace.enabled(), trace.phases(), trace.connection(), trace.tls(), trace.breaches(), trace.withinBudget(), etc.), allowing automated validations inside Goja test blocks.
  • See _examples/trace.http for a runnable pair of requests (one within budget, one deliberately breaching) that demonstrate the timeline output and status messaging.
  • Configure optional OpenTelemetry export with RESTERM_TRACE_OTEL_ENDPOINT (or --trace-otel-endpoint). Additional switches: RESTERM_TRACE_OTEL_INSECURE / --trace-otel-insecure, RESTERM_TRACE_OTEL_SERVICE / --trace-otel-service, RESTERM_TRACE_OTEL_TIMEOUT, and RESTERM_TRACE_OTEL_HEADERS. Spans are emitted only while tracing is enabled; HTTP failures and budget breaches mark the span status as Error.

History and globals

  • The history pane persists responses along with their request and environment metadata. Entries survive restarts (stored under the config directory; see Configuration).
  • Ctrl+G shows current globals (request/file/runtime) with secrets masked. Ctrl+Shift+G (or g Shift+G) clears globals and cookies for the active environment.
  • Ctrl+E opens the environment picker to switch between resterm.env.json (or rest-client.env.json) entries.

Workspaces & Files

  • Resterm scans the workspace root for .http and .rest files. Use --workspace to set the root or rely on the directory of the file passed via --file. Add --recursive to traverse subdirectories (hidden directories are skipped).
  • The navigator filter sits above the tree: press / to focus, type to match files, request/workflow names, URLs, tags, and badges. m toggles method badges (single select) for the highlighted request, t toggles tag badges, and Esc clears text plus any badges.
  • In Git workspaces, supported files show compact status markers in the navigator (M, A, U, D, R, !), and the status bar shows the current branch plus supported-file change counts. Unsupported files are ignored even when they have Git changes.
  • Highlight a request or workflow in the navigator and press l or r to restore the editor pane and jump to that definition.
  • The navigator refreshes immediately when a file is saved or reparsed; filtering auto-loads unopened files so cross-workspace matches still appear. Use Ctrl+Shift+O (or g+Shift+O) to rescan the workspace for new files.
  • Press g+e to open the current file, or the selected navigator file/request/workflow file, in an external editor. Resterm uses $RESTERM_EDITOR, then $VISUAL, then $EDITOR; if none is configured or found, it shows a warning instead of falling back to a non-editor default app.
  • Resterm watches the active file on disk. If another tool edits or deletes it, a modal appears telling you the file changed or went missing. Your in-memory buffer stays intact. Press the reload shortcut (g+Shift+R by default, or whatever you’ve mapped to reload_file_from_disk) to pull the on disk version into the editor. If you have unsaved changes, the first press warns that reload will discard them; press reload again to confirm. Dismiss with Esc to keep your buffer and continue editing.
  • Create a scratch buffer with Ctrl+T for ad-hoc experiments. These buffers are not written to disk unless you save them explicitly.

Inline requests

You can execute simple requests without a .http file:

  1. Type GET https://api.example.com/users (or just the URL) in the editor.
  2. Place the cursor on the line and press Ctrl+Enter.

Inline requests support full URLs and a limited curl import:

curl \
  -X POST https://api.example.com/login \
  -H "Content-Type: application/json" \
  -d '{"user":"demo","password":"pass"}'

Resterm recognizes common curl flags (-X, --request, -H, --header, -d/--data*, --json, --url, -u/--user, --head, --compressed, -F/--form) and converts them into a structured request. Multiline commands joined with backslashes are supported. For larger curl scripts or multiple commands, use the CLI importer (--from-curl).


Variables and Environments

Environment files

Resterm automatically searches, in order:

  1. The directory of the opened file.
  2. The workspace root.
  3. The current working directory.

It loads the first resterm.env.json or rest-client.env.json it finds. The JSON can contain nested objects and arrays—they are flattened using dot and bracket notation (services.api.base, plans.addons[0]).

Example environment (_examples/resterm.env.json):

{
  "dev": {
    "settings.http-root-cas": "dev-ca.pem",
    "settings.grpc-insecure": "false",
    "services": {
      "api": {
        "base": "https://httpbin.org/anything/api"
      }
    },
    "auth": {
      "token": "dev-token-123"
    }
  }
}

Switch environments with Ctrl+E. If multiple environments exist, Resterm defaults to dev, default, or local when available.

Shared variables ($shared)

Use the reserved $shared key to define variables that apply to all environments. This avoids duplicating common values (auth credentials, token URLs, etc.) across every environment. Environment-specific values override $shared when names collide.

{
  "$shared": {
    "api": { "version": "v2" },
    "auth": { "clientId": "demo-client" }
  },
  "dev": {
    "base": { "url": "https://dev.example.com" }
  },
  "prod": {
    "base": { "url": "https://prod.example.com" },
    "auth": { "clientId": "prod-client" }
  }
}

In this example dev inherits auth.clientId=demo-client from $shared, while prod overrides it with prod-client. Both environments receive api.version=v2. The $shared key itself never appears in the environment selector.

Dotenv files via --env-file

Prefer JSON for multi-environment bundles, but you can point Resterm at a dotenv file when you only need a single workspace:

  • Pass --env-file path/to/.env (or .env.prod, prod.env, etc.). Dotenv files are never auto-discovered—explicit opt-in avoids surprising overrides.
  • Supported syntax matches common .env loaders: optional export prefixes, KEY=value pairs, #/; comments, single- and double-quoted values (with escapes), and ${VAR} or $VAR interpolation. We expand references using earlier keys from the same file and the current OS environment.
  • The environment name is derived from a workspace entry (case-insensitive). If that key is missing or blank we fall back to the file name (.env.prodprod, prod.envprod, bare .envdefault).
  • Each dotenv file yields exactly one environment today. If you need multiple environments, stick with resterm.env.json.
  • Limitations: no multi-workspace support, no auto-discovery, and interpolation only sees keys declared above the current line (plus OS envs).

Variable resolution order

When expanding {{variable}} templates, Resterm looks in:

  1. File constants (@const).
  2. Values set by scripts for the current execution (vars.set in pre-request or test scripts).
  3. Request-scope variables (@var request, @capture request).
  4. Runtime globals stored via captures or scripts (per environment).
  5. Document globals (@global, @var global).
  6. File scope declarations and @capture file values.
  7. Selected environment JSON.
  8. OS environment variables (case-sensitive with an uppercase fallback).

Dynamic helpers are also available: {{$uuid}} (alias {{$guid}}), {{$timestamp}} (Unix seconds), {{$timestampMs}} (Unix milliseconds), {{$timestampISO8601}}, and {{$randomInt}}.

Timestamp helpers accept optional offsets: {{$timestamp + 6d}}, {{$timestampISO8601 - 90m}}, {{$timestampMs + 2h}}. Supported units are the standard Go duration units plus d (days) and w (weeks).


SSH Tunnels

Use @ssh to route HTTP/gRPC/WebSocket/SSE traffic through an SSH bastion.

Syntax: # @ssh [scope] [name] key=value ...

  • TL;DR:

    • Define a reusable profile: # @ssh global bastion host=jump.example.com user=ops key=~/.ssh/id_ed25519 persist
    • Use it in a request: # @ssh use=bastion
    • Inline one-off: # @ssh host=10.0.0.5 user=svc password=env:SSH_PW
  • scope: global, file, or request (default request). Global/file scopes define reusable profiles. Requests either reference a profile with use= or define inline options.

  • name: profile tag (default default).

  • Fields: host (required), port (default 22), user, password, key, passphrase, agent (default true when SSH_AUTH_SOCK is present), known_hosts (default ~/.ssh/known_hosts), strict_hostkey (default true), persist (only honored for global/file), timeout, keepalive, retries, use (profile selection).

  • Values expand templates and support env:VAR to prefer terminal env vars before other scopes. Paths for key and known_hosts expand ~ and environment variables.

  • Key is optional: resterm will use your SSH agent (if present) or fall back to default keys (~/.ssh/id_ed25519, id_rsa, id_ecdsa); see "Default key detection" below.

  • Global profiles are shared across the workspace; file-scoped profiles override globals when names collide. use= resolves file profiles first, then globals.

  • Request-level persist is ignored to avoid leaking tunnels. Strict host key checking defaults to true; strict_hostkey=false is allowed but insecure.

Scopes:

  • Global (workspace-wide): # @ssh global bastion host=jump.example.com user=ops key=~/.ssh/id_ed25519 persist
  • File (only this .http): # @ssh file edge host=10.0.0.5 user=ops
  • Request inline (scope keyword optional because request is default): # @ssh host=192.168.1.50 user=svc password=env:SSH_PW timeout=12s
  • Reference a profile: # @ssh use=bastion (picks file-scoped profile first, then global)

Examples:

# @ssh global edge host=env:SSH_BASTION user=ops key=~/.ssh/id_ed25519 persist timeout=30s keepalive=20s
# @global api_host http://10.0.0.10

### List over jump
# @ssh use=edge host={{api_host}} strict_hostkey=false
GET http://{{api_host}}/v1/things

Inline request-only:

### Local jump
# @ssh request host=192.168.1.50 user=svc password=env:SSH_PW timeout=12s
POST http://internal.service/api

gRPC over SSH:

# @ssh use=edge
# @grpc testservices.inventory.ProjectService/Seed
# @grpc-descriptor ./proto/inventory.protoset
GRPC passthrough:///grpc-internal:8082

{}

How it works

SSH tunneling operates at the transport layer, making it transparent to all other features (@trace, @profile, @workflow, @sse, @websocket, @graphql, @grpc, etc.).

Your machine                    Bastion (SSH)                  Private VPC
    │                                  │                            │
    │  1. SSH connect                  │                            │
    ├─────────────────────────────────►│                            │
    │                                  │                            │
    │  2. "Dial 10.0.0.100:80"         │  3. TCP connect            │
    │     (through SSH channel)        ├───────────────────────────►│
    │                                  │                            │
    │  4. HTTP request flows through the tunnel                     │
    │◄─────────────────────────────────────────────────────────────►│

Comparison with terminal tunnels

What you'd do manually:

# Create tunnel in terminal
ssh -L 8080:10.0.0.100:80 ops@bastion.example.com
curl http://localhost:8080/api/users  # in another terminal

What resterm does transparently:

# @ssh global tunnel host=bastion.example.com user=ops key=~/.ssh/id_ed25519 persist

### Hit pod directly through tunnel
# @ssh use=tunnel
GET http://10.0.0.100/api/users

Key difference:

  • Terminal tunnel: bind a local port, then hit localhost:port.
  • Resterm: hit the internal IP directly (10.0.0.100); Resterm dials through the SSH tunnel to reach it.

This makes accessing Kubernetes pods, private VPC resources, or any internal service through a bastion host seamless. The persist option keeps the SSH connection alive so subsequent requests reuse it without reconnection overhead.

Default key detection

When no key is specified, resterm automatically tries these paths in order:

  1. ~/.ssh/id_ed25519
  2. ~/.ssh/id_rsa
  3. ~/.ssh/id_ecdsa

If SSH_AUTH_SOCK is set, the SSH agent is also used by default.


Kubernetes Port-Forwards

Use @k8s to route HTTP/gRPC/WebSocket/SSE traffic through a Kubernetes API port-forward managed by Resterm.

Syntax: # @k8s [scope] [name] key=value ...

  • TL;DR:

    • Define a reusable profile: # @k8s global cluster-api namespace=default service=api port=http context=dev persist
    • Use it in a request: # @k8s use=cluster-api
    • Inline one-off: # @k8s deployment=payments port=https container=api
  • scope: global, file, or request (default request). Global/file scopes define reusable profiles. Requests either reference a profile with use= or define inline options.

  • name: profile tag (default default).

  • Target fields:

    • target= accepts pod:<name>, service:<name>, deployment:<name>, statefulset:<name>.
    • Aliases: pod=, service= (svc=), deployment= (deploy=), statefulset= (sts=).
    • Exactly one target is allowed.
  • Transport fields: namespace (ns), port (number or named port), container, local_port, address, pod_running_timeout, retries, persist (only honored for global/file), context, kubeconfig, use.

  • Values expand templates and support env:VAR to prefer terminal env vars before other scopes.

  • use= resolves file-scoped profile first, then globals.

  • Request-level persist is ignored to avoid leaking background forwarders.

  • @ssh and @k8s are mutually exclusive on a request.

Scopes:

  • Global (workspace-wide): # @k8s global cluster namespace=default service=api port=http context=kind-dev persist
  • File (only this .http): # @k8s file edge namespace=payments deployment=api port=https
  • Request inline (scope keyword optional because request is default): # @k8s service=catalog port=http
  • Reference a profile: # @k8s use=cluster

Examples:

# @k8s global cluster-api namespace=default service=api port=http context=kind-dev persist retries=2

### API through service
# @k8s use=cluster-api
GET http://api.default.svc.cluster.local/v1/health

Named port with deployment target:

### Deployment target
# @k8s deployment=payments-api port=https container=api pod_running_timeout=30s
POST https://payments.internal/v1/charge
Content-Type: application/json

{"amount": 100}

gRPC over Kubernetes port-forward:

# @k8s namespace=default pod=grpc-api-0 port=grpc
# @grpc testservices.inventory.ProjectService/Seed
# @grpc-descriptor ./proto/inventory.protoset
GRPC passthrough:///grpc-api.default.svc.cluster.local:8082

{}

How target resolution works:

  • pod: forwards to that pod directly.
  • service: resolves service selectors and chooses a deterministic pod (running/ready preferred, then by name).
  • deployment / statefulset: resolves selectors from the workload and picks a deterministic pod with the same policy.
  • For named ports:
    • pod/workload targets resolve against container ports (container can disambiguate).
    • service targets resolve service port or targetPort, then map to container port when needed.

Authentication and authorization:

  • Resterm uses client-go kubeconfig loading ($KUBECONFIG/default config, optional kubeconfig= override).
  • context= selects a kube context explicitly.
  • Cluster auth plugins and exec credentials follow your kubeconfig with Resterm's configured exec policy.
  • RBAC still applies. You need permission to read target resources/pods and open pod port-forward sessions in the namespace.

Troubleshooting:

  • k8s target ... has no running pods: check selectors, pod readiness, and namespace.
  • does not expose named port: verify container/service port names and set container= if multiple containers export the same name.
  • service ... has no selector: selector-less services cannot auto-resolve pods.
  • Use a higher pod_running_timeout when pods are still starting.
  • For release hardening, use docs/k8s-release-checklist.md.

Request File Anatomy

Separators and comments

  • Begin each request with a line that starts with ###. Everything up to the next separator belongs to the same request.
  • Lines prefixed with #, //, or -- are treated as comments. Metadata directives live inside these comment blocks.

Metadata directives

DirectiveSyntaxDescription
@name# @name identifierFriendly name used in the navigator, history, and captures.
@const# @const name valueCompile-time constant resolved when the file is loaded; immutable and visible to all requests in the document.
@description / @desc# @description ...Multi-line description (lines concatenate with newline).
@tag / @tags# @tag smoke billingTags for grouping and filters (comma- or space-separated).
@trace# @trace dns<=40ms total<=200ms tolerance=25msEnable per-phase tracing and optional latency budgets.
@no-log# @no-logPrevents the response body snippet from being stored in history.
@log-sensitive-headers# @log-sensitive-headers [true|false]Allow allowlisted sensitive headers (Authorization, Proxy-Authorization, API-token headers such as X-API-Key, X-Access-Token, X-Auth-Key, etc.) to appear in history; omit or set to false to keep them masked (default).
@setting# @setting key valueGeneric settings (transport/TLS today: timeout, proxy, followredirects, insecure, no-cookies, http-*, grpc-*).
@settings# @settings key1=val1 key2=val2 ...Batch settings on one line; supports the same keys as @setting and future prefixes.
@timeout# @timeout 5sEquivalent to @setting timeout 5s.

RestermScript (RST)

RestermScript (RST) powers templates ({{= ... }}) and directive expressions. It is separate from Goja @script blocks and is the default for directive logic. Full reference: docs/restermscript.md.

Request-scoped RST directives

DirectiveSyntaxDescription
@use# @use ./rts/helpers.rts [as helpers]Import an RST module (valid at file or request scope); the alias defaults to the module name.
@patch# @patch file jsonApi {headers: {"Accept":"application/json"}}Define reusable @apply patch profiles at file or global scope.
@apply# @apply {headers: {"X-Test": "1"}}Apply an inline patch to method/url/headers/query/body/auth/settings/vars before pre-request scripts.
@apply# @apply use=jsonApi,use=authProdReuse named @patch profiles; entries run left-to-right and resolve file scope first, then globals.
@when# @when vars.has("token")Run the request only when the expression is truthy.
@skip-if# @skip-if env.mode == "dry-run"Skip the request when the expression is truthy.
@assert# @assert response.statusCode == 200Evaluate an assertion after the response arrives.
@for-each# @for-each json.file("users.json") as userRepeat the request for each item in a list.
@rts pre-request# @rts pre-requestRun a pre-request RST block with request/vars mutation helpers. Use @assert for RST response checks. The full # @script pre-request lang=rts form remains supported.

Workflow-only RST directives

DirectiveSyntaxDescription
@if / @elif / @else# @if last.statusCode == 200 run=StepOKBranch workflow steps based on expressions.
@switch / @case / @default# @switch last.statusCodeChoose a workflow branch based on a switch expression.
@for-each# @for-each json.file("users.json") as userRepeat a workflow step for each item in a list.

Notes:

  • @when / @skip-if gate requests; @if / @switch branch workflows.
  • @for-each is available in both contexts; it repeats a request or a workflow step depending on scope.

Transport settings example

### Fast timeout
# @name TimeoutDemo
# @timeout 2s
GET https://httpbin.org/delay/5

Compare Runs

Run the same request across multiple environments either inline or from the CLI:

  • Add # @compare dev stage prod base=stage to a request block to pin the order/baseline inside the file. Provide at least two environments; base is optional and defaults to the first entry.
  • Supply global defaults with resterm --compare dev,stage,prod --compare-base stage, then press g+c anywhere in the editor to reuse those targets even if the request lacks @compare.
  • While a compare run is active Resterm automatically enables a split layout, pins the previous response in the secondary pane, and streams progress in the status bar (Compare dev✓ stage… prod?). The new Compare tab renders a table with status/code/duration/diff summaries per environment.
  • Each compare sweep writes a bundled history entry (COMPARE method) so you can replay the failing environment later; selecting a compare history row loads the run back into the editor, restores the Compare tab, and lets you resend or inspect deltas off-line.
  • Navigate the Compare tab with ↑/↓ (or PgUp/PgDn/Home/End) to highlight any environment, then press Enter to load that environment’s snapshot into the primary pane while the configured baseline stays pinned in the secondary pane. The Diff tab (and Pretty/Raw/Headers) now reflect “selected ↔ baseline,” so choosing the baseline row yields an “identical” diff, while choosing another environment shows how it diverges from the baseline. To compare against a different reference, rerun with a new base= value or load the desired pair from History.

Use @compare alongside the usual metadata, e.g. to couple request-scoped variables per environment:

### Smoke workflow
# @name smoke
# @compare dev stage prod base=prod
POST {{services.api.base}}/status
Accept: application/json

{
  "env": "{{services.api.name}}"
}

Variable declarations

@const, @var, and @global provide static values evaluated before the request is sent. Constants resolve immediately when the file is parsed and cannot be overridden by captures or scripts; variables follow the usual resolution order and may be updated at runtime.

ScopeSyntaxVisibility
Constant# @const api.root https://api.example.comImmutable for the lifetime of the document; available to every request in the file.
Global# @global api.token value / # @global-secret api.token value / # @var global api.token valueVisible to every request and every file (per environment).
File# @file upload.root https://storage.example.com / # @file-secret upload.root ... / # @var file upload.root ...Visible to all requests in the same document only.
Request# @request trace.id {{$uuid}} / # @request-secret trace.id ... / # @var request trace.id ...Visible only to the current request (useful for tests).

Values are taken verbatim which means that quotes are not special, so # @file greeting "hello world" stores the quotes as part of the value. If you need spaces, just write them directly: # @file greeting hello world.

You can also use shorthand assignments outside comment blocks: @requestId = {{$uuid}}. Shorthand defaults to request scope while you're inside a request block and to file scope elsewhere; add a prefix to override (@global api.token abc, @request trace.id {{$uuid}}, or @file base.url https://example.com).

Append -secret (global-secret, file-secret, request-secret) to mask stored values in summaries; this works for both comment directives and shorthand lines (@global-secret token xyz, @file-secret base.url ..., @request-secret trace.id ...).

Captures

@capture <scope> <name> <expression> evaluates after the response arrives and stores the result for reuse.

Expressions can reference:

  • response.statusCode, response.statusText, response.text()
  • response.headers["Header-Name"] or response.header("Header-Name")
  • response.json.path shorthand (equivalent to response.json().path)
  • stream.kind(), stream.summary().sentCount, stream.events()[0].text for streaming transcripts (available when the request used @sse or @websocket)
  • vars.*, env.*, last.*, imported @use modules, and other RestermScript helpers

Example:

### Seed session
# @name AnalyticsSeedSession
# @capture global-secret analytics.sessionToken = response.json.sessionToken
# @capture file analytics.lastJobId = response.json.jobId
# @capture request analytics.trace = response.headers["x-amzn-trace-id"]
POST https://httpbin.org/anything/analytics/sessions

Template captures such as {{response.json.token}} remain supported and can be used alongside RTS capture expressions.

Set # @setting capture.strict true to make capture-path misses fail instead of silently resolving to an empty string.

Do not mix unquoted template markers and RTS call syntax in the same capture expression (for example contains({{name}}, "x")). Use pure RTS (contains(vars.get("name") ?? "", "x")) or a template expression ({{= contains(...) }}).

capture.strict is the canonical key. capture-strict and capture_strict are accepted for compatibility. When multiple aliases are present, precedence is capture.strict > capture-strict > capture_strict.

Body content

  • Inline: everything after the blank line separating headers and body.
  • External file: < ./payloads/create-user.json loads the file relative to the request file. To also search the workspace root / current working directory, set RESTERM_ENABLE_FALLBACK=1 (opt-in).
  • Inline includes: lines in the body starting with @ path/to/file are replaced with the file contents (useful for multi-part templates).
  • XML/SOAP: inline XML is sent exactly as written after template expansion. XML tags such as <soap:Envelope> are body text, not file references.
  • Forced inline body: add # @body inline (or # @body raw) when a literal body line intentionally looks like a file reference, such as < this is just a string. This only affects parsing. Template expansion and inline includes still work as usual.
  • GraphQL: handled separately (see GraphQL).

Profiling requests

Add # @profile to any request to run it repeatedly and collect latency statistics without leaving the terminal. Profile runs are recorded in history with aggregated results; hit p on the entry to inspect the stored JSON.

### Benchmark health check
# @profile count=50 warmup=5 delay=100ms
GET https://httpbin.org/status/200

Flags:

  • count - number of measured runs (defaults to 10).
  • warmup - optional warmup runs that are executed but excluded from stats.
  • delay - optional delay between runs (e.g. 250ms).

When profiling completes the response pane's Profile tab shows percentiles, histograms, success/failure counts, and any errors that occurred.

Workflows

Group existing requests into repeatable workflows using @workflow blocks. Each step references a request by name and can override variables or expectations.

### Provision account
# @workflow provision-account on-failure=continue
# @step Authenticate using=AuthLogin expect.statuscode=200
# @step CreateProfile using=CreateUser vars.request.name={{vars.workflow.userName}}
# @step FetchProfile using=GetUser

### AuthLogin
POST https://example.com/auth

### CreateUser
POST https://example.com/users

### GetUser
GET https://example.com/users/{{vars.workflow.userId}}

Workflows parsed from the current document appear in the Workflows list on the left. Select one and press Enter (or Space) to run it. Resterm executes each step in order, respects on-failure=continue, and streams progress in the status bar. When the run completes the Workflow tab shows a workflow summary, stable step list, and selected-step response detail. Resterm selects the first failed or canceled step by default, or the first step when everything passes. Press Enter on a selected step to focus its response detail and scroll long responses without changing the selected step. A consolidated entry is written to history so you can review results later.

Key directives and tokens:

  • @workflow <name> starts a workflow. Add on-failure=<stop|continue> to change the default behaviour and attach other tokens (e.g. region=us-east-1) which are surfaced under Workflow.Options for tooling.
  • @description / @tag lines inside the workflow build the description and tag list shown in the UI and stored in history.
  • @step <optional-alias> defines an execution step. Supply using=<RequestName> (required), on-failure=<...> for per-step overrides, expect.status / expect.statuscode, and any number of vars.* assignments.
  • vars.request.* keys add step-scoped values that are available as {{vars.request.<name>}} during that request. They do not rewrite existing @var declarations automatically, so reference the namespaced token (or copy it in a pre-request script) when you want the override.
  • vars.workflow.* keys persist between steps and are available anywhere in the workflow as {{vars.workflow.<name>}}, letting later requests reuse or mutate shared context (e.g. vars.workflow.userId).
  • Unknown tokens on @workflow or @step are preserved in Options, allowing custom scripts or future features to consume them without changing the file format.
  • expect.status supports quoted or escaped values, so you can write expect.status="201 Created" alongside expect.statuscode=201.
  • expect.status / expect.statuscode require non-empty values, and expect.statuscode must be numeric.

Tip: Workflow assignments are expanded once when the request executes. If you need helpers such as {{$uuid}}, place them directly in the request/template or compute them via a pre-request script before assigning the value. Tip: Options are parsed like CLI flags; wrap values in quotes or escape spaces (\ ) to keep text together (e.g. expect.status="201 Created").

Every workflow run is persisted alongside regular requests in History; the newest entry is highlighted automatically so you can open the generated @workflow definition and results from the History pane immediately after the run.

Streaming (SSE & WebSocket)

Streaming sessions surface in the Stream response tab, are captured in history, and can be consumed by captures and scripts.

Server-Sent Events (@sse)

Add # @sse to keep an HTTP request open for events:

### Notifications
# @name notifications
# @sse duration=2m idle=15s max-events=250
GET https://api.example.com/notifications

@sse accepts the following options:

TokenDescription
duration / timeoutMaximum lifetime of the stream. Resterm cancels the request once the timer elapses.
idle / idle-timeoutMaximum quiet period between events before the session is closed.
max-eventsStop reading after N events have been delivered.
max-bytes / limit-bytesCap the total payload size and close once the limit is exceeded.

If the server responds with a non-2xx status or a non-text/event-stream content type, Resterm falls back to a standard HTTP response so you can inspect the error. Successful streams produce a transcript (events plus metadata) that appears in the Stream tab and is saved in history. The summary exposed to templates and scripts includes eventCount, byteCount, duration, and reason (for example eof, timeout, idle-timeout).

WebSockets (@websocket, @ws)

Use # @websocket to negotiate an upgrade, then describe scripted interactions with # @ws lines:

### Chat session
# @name chatSession
# @websocket timeout=10s idle-timeout=4s subprotocols=chat.v2,json compression=true
# @ws send {"type":"hello"}
# @ws wait 1s
# @ws send-json {"type":"message","text":"Hello from Resterm"}
# @ws ping heartbeat
# @ws close 1000 "client done"
GET wss://chat.example.com/room

Available WebSocket options:

TokenDescription
timeoutHandshake deadline (applies until the connection upgrades).
idle-timeoutIdle timeout once the socket is open. Resets on any send or receive activity (0 leaves it unbounded).
max-message-bytesUpper bound on inbound frame sizes.
subprotocolsComma-separated list advertised during the handshake.
compression=<true|false>Explicitly enable or disable per-message compression.

Supported @ws steps:

StepEffect
@ws send <text>Send a UTF-8 text frame. Templates expand before sending.
@ws send-json <object>Encode JSON and send it as text.
@ws send-base64 <data>Decode base64 and send the result as binary.
@ws send-file <path>Send a file from disk (relative to the request file unless absolute).
@ws ping [payload] / @ws pong [payload]Emit control frames (payload limited to 125 bytes).
@ws wait <duration>Pause for the specified duration (e.g. 500ms).
@ws close [code] [reason]Close the connection with an optional status code (defaults to 1000).

Handshake failures surface the HTTP response so upgrade issues are easy to debug. Successful sessions stream events into the UI and history with metadata for direction, opcode, sizes, and close status. The summary exposed to templates and scripts includes sentCount, receivedCount, duration, closedBy, closeCode, and closeReason.

Heads-up: When you keep a WebSocket URL in @const, @global, or @var, write the request line as GET {{ws.url}} (or whichever variable you use). The parser needs the explicit method to recognise the line as a WebSocket request before template expansion. Literal ws:// / wss:// URLs without a method still work when written directly.

Stream tab, history, and console

  • The Stream tab appears automatically whenever a streaming session is active. Scroll to review frames, press b to bookmark important events, and switch tabs with the arrow keys (Ctrl+H / Ctrl+L).
  • While the Stream tab is focused, use g+w then i to toggle the interactive WebSocket console, p to send ping, c to close gracefully, or l to clear the live buffer. If the console is focused for typing, press Esc first. Inside the console, cycle payload modes with F2, send payloads with Ctrl+S or Ctrl+Enter, and reuse previous payloads with the arrow keys.
  • Completed transcripts are saved alongside the request in history with summary headers (X-Resterm-Stream-Type, X-Resterm-Stream-Summary). Scripts and captures can access the same data via stream.* templates and APIs (see Scripting).

Authentication directives

TypeSyntaxNotes
Basic# @auth basic user passInjects Authorization: Basic …. Templates expand inside parameters.
Bearer# @auth bearer {{token}}Injects Authorization: Bearer ….
API key# @auth apikey header X-API-Key {{key}}placement can be header or query. Defaults to X-API-Key header if name omitted.
Custom header# @auth Authorization CustomValueArbitrary header/value pair.
Command# @auth command argv=["gh","auth","token"]Runs a non-interactive command without a shell, parses stdout, and injects a header during auth preparation.
OAuth 2.0# @auth oauth2 token_url=... client_id=...Built-in token acquisition and caching (client_credentials/password/authorization_code + PKCE).

Scopes:

  • Bare @auth ... is request-scoped.
  • @auth request ... is an explicit request-scoped form.
  • @auth file ... defines inherited auth for later requests in the same document.
  • @auth global ... defines workspace-global inherited auth; file-scoped auth wins when both exist.
  • @auth none disables inherited auth for the current request.

OAuth 2.0 parameters

ParameterRequiredDefaultDescription
token_urlYes-Token endpoint URL. Must be provided at least once per cache_key.
auth_urlFor auth code-Authorization endpoint. Required when grant=authorization_code.
client_idYes-Your application's client ID.
client_secretNo-Client secret (omit for public clients using PKCE).
grantNoclient_credentialsGrant type: client_credentials, password, or authorization_code.
scopeNo-Space-separated scopes to request.
audienceNo-Target API audience (Auth0, etc.).
resourceNo-Resource indicator (Azure AD, etc.).
client_authNobasicHow to send credentials: basic (Authorization header) or body (form fields). Falls back to body automatically for public clients.
headerNoAuthorizationWhich header receives the token. Use this when an API expects tokens in a custom header like X-Access-Token.
usernameFor password-Resource owner username (only for grant=password).
passwordFor password-Resource owner password (only for grant=password).
cache_keyNoautoOverride the cache identity. Useful when multiple requests should share the same token even if their parameters differ slightly. When omitted, Resterm derives the key from token URL, client ID, scope, and other fields.
redirect_uriNoautoCallback URL for authorization code flow. See details below.
code_verifierNoautoPKCE verifier (43-128 characters per RFC 7636). Auto-generated when omitted.
code_challenge_methodNos256PKCE method: s256 (recommended) or plain.
stateNoautoCSRF protection token. Auto-generated when omitted.

Any additional key=value pairs are forwarded as extra form parameters to both the authorization and token endpoints.

How token caching works

Resterm caches tokens per environment and cache_key. When a request needs a token:

  1. If a valid cached token exists (not expired, with 30-second safety margin), it's reused immediately.
  2. If the cached token has a refresh_token and is expired, Resterm attempts a refresh.
  3. If refresh fails or no token exists, a fresh token is fetched from the token endpoint.

This means you can define full OAuth parameters once, then reference just cache_key in subsequent requests:

### First request - seeds the cache
# @auth oauth2 token_url={{oauth.tokenUrl}} client_id={{oauth.clientId}} client_secret={{oauth.clientSecret}} scope="read write" cache_key=myapi
GET {{base.url}}/users

### Later request - reuses cached token
# @auth oauth2 cache_key=myapi
GET {{base.url}}/projects

If you skip token_url on a follow-up directive and the cache hasn’t been seeded yet, Resterm will error with @auth oauth2 requires token_url (include it once per cache_key to seed the cache).

Command auth parameters

ParameterRequiredDefaultDescription
argvYes for the first use of a cache_key-JSON array of command arguments. Bare JSON works, for example argv=["gh","auth","token"]. Outer single quotes are also accepted and are useful when you want to preserve whitespace exactly, for example argv='["gh", "auth", "token"]'. Once a cache_key has been seeded, follow-up directives may omit argv and reuse the stored command config.
formatNotextParse stdout as text or json.
headerNoAuthorizationTarget header name.
schemeNoautoExplicit prefix for the final header value. When omitted, Authorization defaults to Bearer, while custom headers get the raw token.
token_pathFor format=json-JSON path to the token value. Uses the same JSON path behavior as RTS captures and helpers.
type_pathNo-Optional token type for Authorization headers when scheme is omitted.
expiry_pathNo-Optional absolute expiry value (RFC3339, RFC3339Nano, Unix seconds, or Unix milliseconds).
expires_in_pathNo-Optional relative lifetime in seconds.
cache_keyNo-Enables in-memory cache reuse per environment and names a reusable command-auth slot. Seed it once with the full command/extraction config, then reuse it with cache_key only on later requests. Reusing the same cache_key with different command/extraction settings is rejected.
ttlNo-Cache fallback TTL when the command output has no expiry fields. Requires cache_key.
timeoutNorequest timeoutPer-command timeout, bounded by the request timeout.

Command auth behavior

  • Resterm runs @auth command without a shell. Pipes, redirects, globbing, and shell interpolation are intentionally not supported.
  • With cache_key, the first full directive seeds a reusable command-auth config for that environment. Later directives can use only cache_key; empty fields inherit from the seeded config.
  • Reusing the same cache_key with different acquisition settings (argv, format, JSON paths, ttl, and related source fields) fails fast. header, scheme, and timeout can still vary per request.
  • Explain preview never executes commands. It only injects a header when a valid cached result already exists.
  • Text mode accepts exactly one non-empty line from stdout. Multi-line output fails with an error and should be switched to format=json.
  • Successful command output is treated as secret. Resterm redacts the raw token and the final injected header value in explain/history views.

Examples:

### GitHub CLI token
# Requires `gh auth login` to be done outside Resterm first.
# @auth command argv=["gh","auth","token"] cache_key=github-cli
GET https://api.github.com/user
Accept: application/vnd.github+json
X-GitHub-Api-Version: 2022-11-28
### Reuse a seeded command-auth slot
# After the first request seeds github-cli, later requests can reference only the cache key.
# @auth command cache_key=github-cli
GET https://api.github.com/user/repos
Accept: application/vnd.github+json
X-GitHub-Api-Version: 2022-11-28
### JSON command output
# @auth command argv=["mycli","auth","print","--json"] format=json token_path=access_token type_path=token_type expires_in_path=expires_in cache_key=myapi
GET https://example.com/projects
### Custom header with TTL fallback
# @auth command argv=["aws","--profile","{{aws.profile}}","ecr","get-login-password"] header=X-Registry-Token cache_key=ecr ttl=10m
GET https://example.com/registry

Scripting (@script)

Add # @script pre-request or # @script test followed by lines that start with >.

# @script pre-request
> var token = vars.global.get("reporting.token") || `script-${Date.now()}`;
> vars.global.set("reporting.token", token, {secret: true});
> request.setHeader("Authorization", `Bearer ${token}`);
> request.setBody(JSON.stringify({ scope: "reports" }, null, 2));

You can also use a brace block ({% ... %}) to avoid prefixing every line with >.

# @script test
> {%
client.test("status ok", function () {
  tests.assert(response.statusCode === 200, "status code");
});
%}

Lines inside the block don't need > (but a leading > is still stripped if present). The {% ... %} block is only for inline script content. Script file includes must be written as their own > < ./path.js line outside the block.

Allowed example:

# @script test
> < ./scripts/pre.js
> {%
client.test("ok", function () {});
%}

Disallowed example:

# @script test
> {%
> < ./scripts/pre.js
client.test("ok", function () {});
%}

Reference external scripts with > < ./scripts/pre.js.

See Scripting API for available helpers.


GraphQL

Enable GraphQL handling with # @graphql (requests start with it disabled). Resterm packages GraphQL requests according to HTTP method:

  • POST: body becomes { "query": ..., "variables": ..., "operationName": ... }.
  • GET: query parameters query, variables, operationName are attached.
  • Template variables in the URL are expanded before the GET parameters are attached, so GET {{graphql.endpoint}} works even when the host is templated.

Available directives:

DirectiveDescription
@graphql [true|false]Enable/disable GraphQL processing for the request.
@operation / @graphql-operationSets the operationName.
@variablesStarts a variables block; inline JSON or < file.json.
@queryLoads the query from a file instead of the inline body.

Example:

### Inline GraphQL Query
# @graphql
# @operation FetchWorkspace
POST {{graphql.endpoint}}

query FetchWorkspace($id: ID!) {
  workspace(id: $id) {
    id
    name
  }
}

# @variables
{
  "id": "{{graphql.workspaceId}}"
}

gRPC

gRPC requests start with a line such as GRPC host:port. Metadata directives describe the method and transport options.

DirectiveDescription
@grpc package.Service/MethodFully qualified method to call.
@grpc-descriptor path/to/file.protosetUse a compiled descriptor set instead of server reflection.
@grpc-reflection [true|false]Toggle server reflection (default true).
@grpc-plaintext [true|false]Force plaintext or TLS.
@grpc-authority valueOverride the HTTP/2 :authority header.
@grpc-metadata key: valueAdd metadata pairs (repeatable).
@setting grpc-root-cas path1,path2Extra root CAs (space/comma/semicolon separated). Paths resolve relative to the request file.
@setting grpc-root-mode append|replaceControl whether extra CAs append to system roots (append) or replace them (replace, default).
@setting grpc-client-cert path / @setting grpc-client-key pathClient cert/key for mTLS (relative paths allowed).
@setting grpc-insecure trueSkip TLS verification (off by default).

Supplying any gRPC TLS setting (roots, client cert/key, insecure) automatically enables TLS unless you explicitly force plaintext with @grpc-plaintext true.

Reserved transport metadata keys (grpc-*, content-type, user-agent, te, etc.) are rejected in @grpc-metadata (and gRPC headers). Use @timeout / @setting timeout to apply deadlines.

The request body contains protobuf JSON. Use < payload.json to load from disk, and add # @body expand if the file includes templates. Responses display message JSON, headers, and trailers; history stores method, status, and timing alongside HTTP calls.

Streaming (server/client/bidi) is supported. Unary/server streaming requests use a single JSON object, while client/bidi streaming requests send a JSON array of message objects. Streaming responses return a JSON array, and the Stream tab shows a per-message transcript with a summary.

Example:

### Generate Report Over gRPC
# @timeout 5s
# @grpc analytics.ReportingService/GenerateReport
# @grpc-reflection true
# @grpc-plaintext true
# @grpc-authority analytics.dev.local
# @grpc-metadata x-trace-id: {{$uuid}}
# @setting grpc-root-cas ./ca.pem
GRPC {{grpc.host}}

{
  "tenantId": "{{tenant.id}}",
  "reportId": "rep-{{$uuid}}"
}

Streaming example:

### Bidi Stream Chat
# @grpc chat.ChatService/Stream
# @grpc-plaintext true
GRPC {{grpc.host}}

[
  {"message": "hello"},
  {"message": "again"}
]

Scripting API

Scripts run in an ES5.1-compatible Goja VM.

Pre-request scripts (@script pre-request)

Objects:

  • request
    • getURL(), setURL(url)
    • getMethod(), setMethod(method)
    • getHeader(name), setHeader(name, value), addHeader(name, value), removeHeader(name)
    • setBody(text)
    • setQueryParam(name, value)
  • vars
    • get(name), set(name, value), has(name)
    • global.get(name), global.set(name, value, options), global.has(name), global.delete(name) (options.secret masks values)
  • console.log/warn/error (no-op placeholders for compatibility)

Return values from set* helpers are ignored; side effects apply to the outgoing request.

Test scripts (@script test)

Objects:

  • client.test(name, fn) – registers a named test. Exceptions or manual failures mark the test as failed.
  • tests.assert(condition, message) – add a pass/fail entry.
  • tests.fail(message) – explicit failure.
  • response
    • status, statusCode, url, duration
    • body() (raw string)
    • json() (parsed JSON or null)
    • headers.get(name), headers.has(name), headers.all (lowercase map)
  • stream
    • enabled() – returns true when the current response is an SSE or WebSocket transcript.
    • kind() – returns "sse" or "websocket".
    • summary() – copy of the transcript summary (sentCount, receivedCount, eventCount, duration, etc.).
    • events() – array of event objects (data/comment for SSE, type/text/base64/direction for WebSockets).
    • onEvent(fn) – registers a callback invoked for each event after the script runs; useful for assertions over the entire stream.
    • onClose(fn) – registers a callback invoked once with the summary after all events replay.
  • vars – same API as pre-request scripts (allows reading request/file/global values and writing request-scope values for assertions).
  • vars.global – identical to pre-request usage; changes persist after the script.
  • console.* – same placeholders as above.

Example test block:

# @script test
> client.test("captures token", function () {
>   var token = vars.get("oauth.manualToken");
>   tests.assert(!!token, "token should be available");
> });

Authentication

Static tokens

Use @auth bearer {{token}} or Authorization: Bearer {{token}} headers. Combine with @global or environment values for reuse.

When you want one auth definition to apply to many requests, scope it:

# @auth file bearer {{auth.token}}

### Inherits file auth
GET {{base.url}}/profile

### Opt out for one request
# @auth none
GET {{base.url}}/public

Captured tokens

Capture values at runtime and reuse them in subsequent requests:

### Login
# @capture global-secret auth.token = response.json.token
POST {{base.url}}/login

{
  "user": "{{user.email}}",
  "password": "{{user.password}}"
}

### Authorized request
# @auth bearer {{auth.token}}
GET {{base.url}}/profile

OAuth 2.0 directive

Resterm handles the full OAuth 2.0 token lifecycle: fetching tokens, caching them per environment, refreshing when expired, and injecting the Authorization: Bearer ... header automatically. Three grant types are supported.

Client credentials grant

Best for machine-to-machine authentication where no user is involved.

### Service-to-service call
# @auth oauth2 token_url=https://auth.example.com/oauth/token client_id={{svc.clientId}} client_secret={{svc.clientSecret}} scope="api:read api:write"
GET https://api.example.com/internal/status

By default, credentials are sent via HTTP Basic authentication. Use client_auth=body to send them as form fields instead (required by some providers):

# @auth oauth2 token_url={{oauth.tokenUrl}} client_id={{oauth.clientId}} client_secret={{oauth.clientSecret}} scope="{{oauth.scope}}" client_auth=body
GET {{base.url}}/resource

Password grant

For legacy systems that require username/password authentication.

### Resource owner password
# @auth oauth2 token_url=https://auth.example.com/oauth/token client_id={{app.clientId}} client_secret={{app.clientSecret}} grant=password username={{user.email}} password={{user.password}} scope="profile"
GET https://api.example.com/me

Authorization code + PKCE

When you use grant=authorization_code, Resterm handles the entire OAuth automatically:

  1. Browser launch - Opens your system browser to auth_url with the authorization request.
  2. Local callback server - Spins up a temporary HTTP server on localhost to capture the redirect.
  3. Code exchange - Exchanges the authorization code for tokens at token_url, including the PKCE verifier.
  4. Token injection - Caches the token and injects it into your request.
Redirect URI behavior

The redirect_uri controls where the authorization server sends the user after login:

ConfigurationResult
Omit redirect_urihttp://127.0.0.1:<random-port>/oauth/callback
redirect_uri=http://127.0.0.1:8080/callbackUses port 8080 with path /callback
redirect_uri=http://localhost:0/authRandom port, custom path /auth

Constraints:

  • Must use http:// scheme (not https://) - RFC 8252
  • Host must be 127.0.0.1 or localhost - external hosts are rejected
  • Register the redirect URI pattern with your OAuth provider (most allow http://127.0.0.1:* or similar)
PKCE details

PKCE (Proof Key for Code Exchange) protects against authorization code interception. Resterm generates these automatically:

  • code_verifier - 64 random bytes, base64url-encoded (~86 characters). You can provide your own if needed (must be 43-128 characters per RFC 7636).
  • code_challenge - SHA-256 hash of the verifier, base64url-encoded.
  • state - 24 random bytes for CSRF protection.
Example: Public client with PKCE
### GitHub OAuth (public client, no secret)
# @auth oauth2 auth_url=https://github.com/login/oauth/authorize token_url=https://github.com/login/oauth/access_token client_id={{github.clientId}} scope="repo read:user" grant=authorization_code
GET https://api.github.com/user
Accept: application/json
Example: Confidential client
### Auth0 with client secret
# @auth oauth2 auth_url=https://{{auth0.domain}}/authorize token_url=https://{{auth0.domain}}/oauth/token client_id={{auth0.clientId}} client_secret={{auth0.clientSecret}} scope="openid profile" audience={{auth0.audience}} grant=authorization_code
GET {{api.url}}/userinfo
Timeout behavior

Authorization code flow has a 2-minute timeout by default (to give users time to complete login in the browser). If you need longer, the request's @timeout setting is respected as long as it exceeds 2 minutes.

Custom token header

Some APIs expect tokens in a non-standard header. Use the header parameter to change where the token goes:

### API expecting X-Access-Token header
# @auth oauth2 token_url={{oauth.tokenUrl}} client_id={{oauth.clientId}} client_secret={{oauth.clientSecret}} header=X-Access-Token
GET https://api.example.com/data

When header is set to something other than Authorization, Resterm injects just the raw token (without the "Bearer " prefix). When using the default Authorization header, the full Bearer <token> format is used.

Command-backed auth

Use @auth command when your existing CLI already knows how reterive or print tokens.

To apply command auth to every request in a file, define it once with file scope:

# @auth file command argv=["gh","auth","token"] cache_key=github-cli

### User
GET https://api.github.com/user

### Repos
GET https://api.github.com/user/repos

### Public endpoint without inherited auth
# @auth none
GET https://api.github.com/rate_limit
### Seed a reusable command-auth slot
# @auth command argv=["gh","auth","token"] cache_key=github-cli
GET https://api.github.com/user
Accept: application/vnd.github+json
### Reuse it later with cache_key only
# @auth command cache_key=github-cli
GET https://api.github.com/user/repos
Accept: application/vnd.github+json

Structured output works too:

### Internal CLI with JSON output
# @auth command argv=["mycli","auth","print","--json"] format=json token_path=access_token type_path=token_type expires_in_path=expires_in cache_key=myapi
GET https://api.example.com/projects

Key points:

  • Commands run during auth preparation, before the request is sent.
  • The command inherits Resterm's environment and runs with the request file's directory as its working directory.
  • Interactive login flows are not supported. Authenticate the CLI outside Resterm first, then use the non-interactive token-printing command.
  • Custom headers are supported with header=...; Authorization defaults to Bearer <token>.
  • Add cache_key when you want preview support and in-memory reuse across requests in the same environment.
  • cache_key now behaves like OAuth: it names a reusable slot. Seed it once, then reuse it. If the cache has not been seeded yet, Resterm errors with @auth command requires argv (include it once per cache_key to seed the cache).

HTTP Transport & Settings

  • Global defaults are passed via CLI flags (--timeout, --follow, --insecure, --proxy).
  • Per-request overrides use @setting, @settings, or @timeout.
  • HTTP version: @setting http-version 1.1 (accepts 1.0, 1.1, 2, HTTP/1.1, HTTP/2). A trailing HTTP/1.1 on the request line also sets the version; explicit settings win. 2 is strict and fails if the response is not HTTP/2. WebSocket requests are incompatible with 1.0 and 2.
  • Requests use an in-memory cookie jar per environment. Cookies are isolated between environments, and @setting no-cookies true disables cookies for a request without clearing the stored jar. Use Ctrl+Shift+G (or g Shift+G) to clear cookies for the current environment.
  • TLS per request: # @settings http-root-cas=a.pem http-client-cert=cert.pem http-client-key=key.pem http-insecure=true for a single line, or @setting key value per line (http-root-cas accepts space/comma/semicolon separated lists; paths are relative). GraphQL/REST/WebSocket/SSE all share these HTTP settings.
  • Use @no-log to omit sensitive bodies from history snapshots.
  • History is stored in ${RESTERM_CONFIG_DIR}/history.db (defaults to the platform config directory) and has no fixed entry cap. Set RESTERM_CONFIG_DIR to relocate it.
  • On first launch after upgrading, Resterm imports ${RESTERM_CONFIG_DIR}/history.json into history.db automatically when present.
  • If the SQLite history file is detected as corrupted, Resterm quarantines it to history.db.corrupt-<timestamp> and initializes a fresh history.db.
  • Custom root CAs replace system roots by default (strict). Set http-root-mode append or grpc-root-mode append if you want to keep system roots in addition to your own.
  • File-level defaults: place # @setting key value or # @settings key1=val1 ... before the first request to apply to all requests in that file. Request-level overrides still win.
  • Settings are generic. Today the recognized prefixes are transport/TLS (http-*, grpc-*, timeout, proxy, followredirects, insecure, no-cookies). Future features can add more prefixes; unknown keys are ignored for now to stay forward-compatible.
  • Environment defaults: resterm.env.json can carry global settings under the settings. prefix (e.g., "settings.http-root-cas": "ca-dev.pem", "settings.grpc-insecure": "false"). Precedence is global (env) < file < request.
  • OAuth token exchanges reuse the same HTTP TLS settings (root CAs, client cert/key, http-insecure) as the main request.

Body helpers:

  • < path loads file contents as the body.
  • Inline XML/SOAP tags are treated as body text.
  • # @body inline or # @body raw forces prefixed body lines to remain inline text.
  • @ path inside the body injects file contents inline.
  • GraphQL payloads are normalized automatically.

Collection Sharing

Resterm can export a portable, self-contained collection bundle that you can commit to Git and import anywhere else. This is useful when you want a reliable "same inputs, same requests" handoff between developers, CI jobs, or support environments.

A bundle export contains your request files plus the files those requests depend on, such as RTS modules, script includes, payload files, GraphQL query/variables files, gRPC descriptor/message files, and WebSocket send-file payloads. Resterm writes a manifest.json file with per-file checksums, and import verifies those checksums before writing to disk.

What happens to environment files

Resterm treats environment sharing as an explicit safe template flow:

  1. If resterm.env.example.json exists in the workspace, Resterm exports it exactly as written.
  2. If only resterm.env.json or rest-client.env.json exists, Resterm generates resterm.env.example.json and replaces every value with REPLACE_ME.
  3. If no environment file exists, Resterm still writes an empty resterm.env.example.json so the bundle shape remains predictable.

Export a collection bundle

The following command exports recursively and names the bundle:

resterm collection export \
  --workspace ./my-api \
  --out ./shared/my-api-bundle \
  --recursive \
  --name "my-api-v1"

A typical bundle directory looks like this:

shared/my-api-bundle/
  manifest.json
  requests.http
  rts/helpers.rts
  payloads/create-user.json
  resterm.env.example.json

You can commit this directory directly to Git and open it in code review like any other project files.

Import a collection bundle

You can import that bundle into a local workspace with:

resterm collection import \
  --in ./shared/my-api-bundle \
  --workspace ./my-local-api

If you want to inspect the plan before writing, run:

resterm collection import \
  --in ./shared/my-api-bundle \
  --workspace ./my-local-api \
  --dry-run

If destination files already exist and replacement is intentional, you can add --force.

Pack a bundle into a zip archive

If you want to hand off a single file instead of a directory, you can pack an existing bundle:

resterm collection pack \
  --in ./shared/my-api-bundle \
  --out ./shared/my-api-bundle.zip

This command reads and validates the bundle manifest and payload checksums before writing the archive, so you do not package a partially corrupted bundle by accident.

Unpack a zip archive back into a bundle directory

You can unpack the archive before importing it:

resterm collection unpack \
  --in ./shared/my-api-bundle.zip \
  --out ./shared/my-api-bundle

Unpack validates archive paths, rejects unsafe entries (for example traversal paths and symlinks), validates checksums against manifest.json, and only then moves the unpacked bundle into place.

Safety and validation behavior

Export, import, pack, and unpack all enforce path safety and integrity checks. Resterm rejects references that escape the workspace, rejects malicious traversal paths in manifests and archives, rejects symlink escapes, and fails operations if size or checksum validation does not match the manifest.


Response History & Diffing

  • Every successful request produces a history entry with request text, method, status, duration, and a body snippet (unless @no-log is set). Values injected from -secret captures and allowlisted sensitive headers (Authorization, Proxy-Authorization, X-API-Key, X-Access-Token, X-Auth-Key, X-Amz-Security-Token, etc.) are masked automatically unless you opt-in with @log-sensitive-headers.
  • History entries are environment-aware; selecting another environment filters the list automatically.
  • When focused on the history list, press Enter to load a request into the editor without executing it. Use r/Ctrl+R (or your normal send shortcut such as Ctrl+Enter / Cmd+Enter) to replay the loaded entry.
  • The Diff tab compares focused versus pinned panes, making regression analysis straightforward.
  • Compare runs are stored as grouped rows (COMPARE method). The preview (p) shows the entire bundle, Enter loads the failing (or baseline) environment back into the editor, and the Compare tab is automatically repopulated so you can audit deltas offline.

CLI Reference

Resterm now has a dedicated CLI guide: cli.md.

Use it for:

  • resterm run selectors, formats, body-only output, artifacts, persisted state, and exit codes
  • resterm init, collection, and history subcommands
  • shared execution flags such as --workspace, --env, --timeout, --proxy, and --compare
  • curl and OpenAPI import flows

Common entry points:

resterm
resterm run ./requests.http
resterm init ./api-tests
resterm history stats
resterm collection export --workspace ./api --out ./shared/api-bundle

Configuration

  • Config directory: $HOME/Library/Application Support/resterm (macOS), %APPDATA%\resterm (Windows), or $HOME/.config/resterm (Linux/Unix). Override with RESTERM_CONFIG_DIR.
  • History file: <config-dir>/history.db (no fixed entry limit).
  • Settings file: <config-dir>/settings.toml (created when you first change preferences such as the default theme).
  • Theme directory: <config-dir>/themes/ (override with RESTERM_THEMES_DIR). Drop .toml or .json files here to make them available in the selector.
  • Runtime globals and file captures are scoped per environment and document; they are released when you clear globals or switch environments.

Theming

Resterm lets you override its Lip Gloss styles. Fonts and ultimate colour rendering still come from your terminal emulator; the theme controls which colours Resterm asks the terminal to use.

Where themes live

  • Default directory: <config-dir>/themes/
  • Override with RESTERM_THEMES_DIR
  • Sample: _examples/themes/aurora.toml
  • Switch at runtime with Ctrl+Alt+T (or press g then t). The selection persists in settings.toml.

Theme anatomy

Theme files can be TOML or JSON. Unspecified fields inherit defaults.

[metadata]
name = "Oceanic"
author = "You"
description = "Cool dusk palette"

[styles.header_title]
foreground = "#5fd1ff"
bold = true

[colors]
pane_border_focus_file = "#1f6feb"
pane_border_focus_editor = "#56a9dd"
pane_border_focus_response = "#33c481"
pane_active_foreground = "#f8faff"

[editor_metadata]
comment_marker = "#4c566a"

[[header_segments]]
background = "#5e81ac"
foreground = "#eceff4"

[[command_segments]]
background = "#3b4252"
key = "#88c0d0"
text = "#eceff4"

[status_bar]
base = "#000000"

[status_bar.info]
foreground = "#eff6ff"
background = "#2563eb"

[status_bar.file]
foreground = "#f9fafb"
background = "#404040"

Sections and fields

SectionKeysNotes
[metadata]name, description, author, version, tags[]Informational only; shown in the selector.
[styles.*]browser_border, editor_border, response_border, navigator_title, navigator_title_selected, navigator_subtitle, navigator_subtitle_selected, navigator_badge, navigator_tag, navigator_detail_title, navigator_detail_value, navigator_detail_dim, app_frame, header, header_title, header_value, header_separator, status_bar, status_bar_info, status_bar_key, status_bar_value, command_bar, command_bar_hint, cli_run_picker, cli_run_picker_selected, cli_run_picker_cursor, cli_run_picker_cursor_selected, response_search_highlight, response_search_highlight_active, tabs, tab_active, tab_inactive, notification, error, success, header_brand, command_divider, pane_title, pane_title_file, pane_title_requests, pane_title_editor, pane_title_response, pane_divider, editor_hint_box, editor_hint_item, editor_hint_selected, editor_hint_annotation, list_item_title, list_item_description, list_item_selected_title, list_item_selected_description, list_item_dimmed_title, list_item_dimmed_description, list_item_filter_match, response_content, response_content_raw, response_content_headers, response_selection, response_cursor, stream_content, stream_timestamp, stream_direction_send, stream_direction_receive, stream_direction_info, stream_event_name, stream_data, stream_binary, stream_summary, stream_error, stream_console_title, stream_console_mode, stream_console_status, stream_console_prompt, stream_console_input, stream_console_input_focusedAccept foreground, background, border_color, border_background, border_style (normal, rounded, thick, double, ascii, block), plus booleans bold, italic, underline, faint, strikethrough, and align (left, center, right).
[colors]pane_border_focus_file, pane_border_focus_requests, pane_border_focus_editor, pane_border_focus_response, pane_active_foreground, git_branch, git_modified, git_added, git_untracked, git_deleted, git_renamed, git_conflict, method_get, method_post, method_put, method_patch, method_delete, method_head, method_options, method_grpc, method_ws, method_defaultFrequently reused colours for pane borders, active text, Git markers, and method badges.
[status_bar]base, plus [status_bar.info], [status_bar.warn], [status_bar.error], [status_bar.success], [status_bar.file], [status_bar.focus], [status_bar.mode], [status_bar.editor], [status_bar.zoom], [status_bar.minimized], [status_bar.version], [status_bar.user], [status_bar.host] with foreground and backgroundControls the segmented bottom status bar. Segment entries inherit the built-in palette. [status_bar.editor] colours the editor cursor position (Ln/Col), shown faint by default; set it to override. base fills otherwise empty status-bar cells when set; omit it to leave them uncoloured.
[editor_metadata]comment_marker, directive_default, value, setting_key, setting_value, request_line, request_separator, [editor_metadata.directive_colors]Controls metadata highlighting inside the editor.
[[header_segments]]background, foreground, border, accentRotating header chips; add multiple tables for rotation.
[[command_segments]]background, border, key, textColour sets for command bar hint capsules.

The segmented bottom bar uses [status_bar] for section backgrounds and text colours. Legacy [styles.status_bar*] entries remain accepted for older themes, but they do not control the segmented section palette.

Setting editor_metadata.directive_default recolours every built-in directive (@name, @tag, etc.) that still uses the inherited default. Specify entries inside [editor_metadata.directive_colors] only when you need a directive to diverge from that default.

Set editor_metadata.request_line to recolour the full request line (POST https://…). If you omit it, Resterm falls back to the directive default. Use editor_metadata.request_separator for the ### section dividers and editor_metadata.comment_marker for the # / // prefixes at the start of comment lines.

styles.stream_* keys control the transcript viewer (events, timestamps, direction badges). styles.stream_console_* tweak the interactive WebSocket console (prompt, status line, input field).

styles.cli_run_picker* keys only affect the interactive resterm run request picker. If they are omitted, the picker inherits the general list item styles where possible.

navigator_* styles control the unified sidebar tree, and styles.list_item_* keys continue to power history/picker list rows. styles.response_content, styles.response_content_raw, styles.response_content_headers, styles.response_selection, and styles.response_cursor colour the response panes (with the general key applied first, then the tab-specific override for Raw and Headers). Pane border titles inherit the active or inactive border colour by default; set a pane_title_* foreground when you want a title colour to diverge.

Testing a theme

export RESTERM_THEMES_DIR="$(pwd)/_examples/themes"
resterm

Inside Resterm, press g then t (or Ctrl+Alt+T) and pick “Aurora” for a dark setup or “Daybreak” for light terminals. Quit and restart to confirm the theme persists. If a theme fails to parse, Resterm logs the error and falls back to the default palette.


Examples

Explore _examples/ for ready-to-run:

  • basic.http - simple GET/POST with bearer auth.
  • scopes.http - demonstrates global/file/request captures.
  • scripts.http - pre-request and test scripting patterns.
  • xml.http - XML bodies and explicit inline angle-prefixed text.
  • graphql.http - inline and file-based GraphQL requests.
  • grpc.http - gRPC reflection and descriptor usage.
  • k8s.http - Kubernetes profile scopes, non-pod targets, named ports, and gRPC over @k8s.
  • auth_scopes.http - default auth inheritance across global/file/request scopes plus @auth none.
  • auth_command.http - command-backed auth with global defaults, gh auth token, patch-based reuse, JSON output parsing, and custom header examples.
  • oauth2.http - manual capture vs using the @auth oauth2 directive.
  • transport.http - timeout, proxy, and @no-log samples.
  • compare.http - demonstrates @compare directives and CLI-triggered multi-environment sweeps.
  • workflows.http - end-to-end workflow with captures, overrides, and expectations.
  • curl-import.http - sample output generated from curl commands via the CLI importer.

Open one in Resterm, switch to the appropriate environment (resterm.env.json), and send requests to see each feature in action.


Troubleshooting & Tips

  • Use Ctrl+P to force a reparse if the navigator seems out of sync with editor changes.
  • If a template fails to expand (undefined variable), Resterm leaves the placeholder intact and surfaces an error banner.
  • Combine @capture request ... with test scripts to assert on response headers without cluttering file/global scopes.
  • Inline curl import works best with single commands; complex shell pipelines may need manual cleanup.
  • Ctrl+Shift+V pins the focused response pane-ideal for diffing the last good response against the current attempt.
  • Keep secrets in environment files or runtime globals marked as -secret. Remember that history stores the raw response unless you add @no-log or redact the payload yourself.

For additional questions or feature requests, open an issue on GitHub.