Postguy
Guide

Variables

Use variables in requests. Scopes, precedence, environments, globals, secrets, dynamic variables and body shortcodes.

A variable is a named value. You write it into a request as {{name}}, and Postguy replaces it when the request is sent. Use variables for base URLs, tokens, ids and anything that changes between environments.

{{baseUrl}}/users/{{userId}}

Syntax rules:

  • Names are matched exactly as written, including case. Spaces inside the braces are part of the name. Write {{baseUrl}}, not {{ baseUrl }}.
  • You can use a variable in the URL, query and path-variable rows, header names and values, every body mode, and the fields of authorization.
  • A value can contain other variables. They expand recursively to a depth of 10. A name that refers back to itself, directly or through a chain, is left as literal text and flagged as unresolved.
  • A name that no scope defines is left as literal text. For example, {{missing}} is sent as {{missing}}. If the unresolved name is in the URL's host, the send fails with an error instead.
  • A variable whose row is disabled, or whose name is empty, does not exist.
  • {{% starts a shortcode, not a variable.

Precedence

When the same name is defined in several scopes, the highest rank wins.

RankScopeWhere it comes from
0 (lowest)GlobalsThe workspace-wide Globals tab
1CollectionThe collection's Variables tab
2FolderA folder's Variables tab. Outer folders first, so the innermost folder wins.
3EnvironmentThe active environment
4DataThe current row of a runner data file
5RuntimeValues set by scripts with pg.setVar or bru.setVar. They live for the app session and are never written to disk.
6 (highest)LocalValues set by pm.variables.set during one send
globals      baseUrl = https://api.example.com
environment  baseUrl = http://localhost:8080     <- wins over globals
request URL  {{baseUrl}}/health  ->  http://localhost:8080/health

The folders and the collection come from the request's position in the tree. See Collections.

Scripts can read and write several scopes. See Scripts and tests and the scripting API. Folder variables and the data row are read-only to scripts.

Environments and globals

An environment is a named set of variables, for example Local, Staging or Production. Only one environment is active at a time.

Choose the active environment from the selector at the right end of the tab strip, or pick No environment. The active environment applies to every request in the workspace.

  • Create an environment with + Create new environment at the bottom of the selector, or from the sidebar.
  • The ... menu in the editor has Duplicate, Export and Delete. Each environment has a colour dot.
  • The editor tab has a table with an enabled checkbox, name, value and an optional description. You can search, drag to reorder, and add a row by typing in the empty row at the end. Changes save automatically.
  • Globals is a single set of variables for the whole workspace. Open it with the Globals button in the bottom bar. It has the same editor and the lowest precedence.
  • The Vars button next to the selector opens a quick look at the active environment, the globals and the current collection's variables. Secret values are masked until you reveal them. Click a row to copy its value.
  • Collections and folders hold their own variables in their detail tabs.
The quick look is read-only. You cannot edit a value or add a variable from it. Two features are planned and not available: setting a variable from selected text in a field, and a pinned environment per collection.

Chips and autocomplete

Fields that accept variables tint each {{name}}. These fields are:

  • The URL bar.
  • The key and value cells of the Query Params, Path Variables, Headers, form-data and x-www-form-urlencoded tables.
  • The raw and GraphQL body editors.
TintMeaning
Blue (accent colour)Resolved from a scope, or a known dynamic variable such as {{$guid}}
Red (danger colour)Not defined in any visible scope, or an unknown $name

Hover a chip to see its name, scope and value. Secret values stay masked until you hold Cmd/Ctrl.

Type {{ in the URL field to open a list of the variables you can reach. It shows up to eight at a time and filters as you type.

Dynamic variables are not in the autocomplete list yet. The hover card cannot edit a value or add the variable to a scope.

Secret variables

Mark a row Secret in an environment, the globals, a collection or a folder. A secret variable resolves like any other, with these differences:

  • The value is stored in .postguy/secrets.json in the workspace, never in the collection or environment file. The file is mode 0600 on Unix.
  • The value is masked in the editor. Use Reveal secrets, or hold Cmd/Ctrl, to see it.
  • Wherever Postguy echoes a request, each occurrence of a secret's value is replaced with •, one per character and at most 16. This covers the Console, the request saved in History, script failure messages and the recorded URL and headers.
  • Environment and globals exports leave secret values out unless you explicitly include them. See Import and export.
  • Code snippets keep {{variables}} unresolved, so they do not contain the value.
Secrets are kept apart from your collections, but they are not encrypted at rest. Anyone who can read the workspace folder can read .postguy/secrets.json. Keep that file out of version control and out of backups you share.Masking only hides values in what Postguy displays. If a server echoes a secret back in a response body, you see it as received.

A secret row whose value is missing from the secrets file resolves to an empty string.

Dynamic variables

Names that start with $ are generated when the request is sent. Each occurrence gets a new value.

{
  "id": "{{$guid}}",
  "createdAt": "{{$isoTimestamp}}",
  "email": "{{$randomEmail}}",
  "quantity": {{$randomInt}}
}
NameValue
$guid, $randomUUIDA random UUID v4
$timestampThe current Unix time in seconds
$isoTimestampThe current time as ISO 8601 UTC with milliseconds
$randomIntAn integer from 0 to 1000

More are available for names, people, places, internet values, dates, finance, text and files. Their values come from word lists written for Postguy. An unknown $name is left as literal text. See the full list in Dynamic variables.

Shortcodes

A shortcode puts a computed value, or the content of a collection asset, into a request body. Write it between {{% and %}}.

{{% base64(file('lib/a.jpg')) %}}
FunctionResult
file('path')The bytes of the asset at path. At the top level the bytes must be UTF-8 text. Otherwise wrap them in another function.
text('path')The asset as UTF-8 text. An asset that is not UTF-8 is an error.
base64(x)Standard Base64 of x, with padding
base64url(x)URL-safe Base64 of x, without padding
hex(x)Lowercase hexadecimal of x
sha256(x)SHA-256 of x as lowercase hex
md5(x)MD5 of x as lowercase hex

Grammar:

  • A call is name(arg, ...). Names are lowercase letters and digits.
  • file and text take exactly one quoted path. The other functions take exactly one argument: a quoted string or another call.
  • Strings use '...' or "...". Escape a backslash, a single quote or a double quote with a backslash.
  • Whitespace is allowed around the call, so {{%base64(file('a'))%}} also works.
  • {{variables}} inside a string argument resolve first. So {{% text('{{dir}}/note.txt') %}} reads from the folder that dir names.

Limits:

LimitValue
Call nesting8
One shortcode4,096 bytes
Shortcodes in one text1,000
Expanded output256 MiB

Where shortcodes apply

WhereShortcodes
Raw body, x-www-form-urlencoded values, form-data text valuesYes
GraphQL query, variables and operation nameYes
Form-data file rows and the Binary body pathOnly the exact form {{% file('name') %}}, which sends the asset
URL, query and path rows, headers, authorizationNo

Shortcodes expand first. The result then goes through normal {{variable}} resolution.

They need a stored request, because assets belong to a collection. Assets live in <workspace>/assets/<collection id>/. Manage them in the collection's Assets tab.

Errors and colours:

  • An invalid shortcode, a missing asset or a binary result without base64(...) stops the send with an error that names the problem.
  • The editor marks that shortcode red and shows the reason.
  • Valid shortcodes are blue.

An asset is capped at 100 MiB. An import is capped at 2 GiB or 10,000 files.

{
  "name": "avatar",
  "data": "{{% base64(file('images/avatar.png')) %}}",
  "checksum": "{{% sha256(text('images/avatar.txt')) %}}"
}
  • env_list, env_create, env_set_active, env_get, env_set_var and env_unset_var manage environments.
  • globals_get and globals_set_var manage globals.
  • Both set tools take secret: true to keep a value in the secrets file.
  • variables_resolve resolves a template and reports each variable's scope, secret flag and unresolved state.
  • runtime_variables reads the values scripts set at runtime.
  • asset_list, asset_import, asset_rename and asset_delete manage the files shortcodes read.
Tools take ids, not names. See Automation tools.
Copyright © 2026