Dynamic values

Generate a fresh UUID, timestamp, email or random number, or sign and encode values inline: the $-prefixed helpers in every ${{ }} template.

5 min read

A test that posts the same email address twice fails the second time. A signed request needs an HMAC over a body you only assembled a moment ago. A correlation header wants a fresh id per call.

Every JavaScript scope a flow can write has a set of $-prefixed helper functions for exactly this. They need no import, no script node, and no dependency:

- id: create_user
  type: request
  config:
    method: POST
    url: ${{ variables.baseUrl }}/users
    headers:
      X-Request-Id: ${{ $uuid() }}
      X-Signature: ${{ $hmac(variables.payload, env.SIGNING_KEY) }}
    body: |
      {
        "email": "${{ $randomEmail() }}",
        "name": "${{ $randomFullName() }}",
        "createdAt": "${{ $isoTimestamp() }}"
      }
  next: null

Where they work

Everywhere a flow can run JavaScript, which is more places than it first sounds:

  • ${{ }} interpolation in a URL, header, query or path param, or body
  • a script node’s code
  • every hook phase
  • an assertion’s check, a condition’s expression, a loop’s over
  • a capture expression

They are ordinary function calls, so the parentheses are not optional: ${{ $uuid }} interpolates the function itself, not a value. Write ${{ $uuid() }}.

Identity and time

HelperReturns
$uuid()A v4 UUID
$timestamp()Unix epoch milliseconds, a number
$isoTimestamp()An ISO 8601 string, for example 2026-08-08T12:34:56.789Z

Random values

HelperReturns
$randomInt(min, max)Integer in [min, max], both ends included
$randomFloat(min, max)Float in [min, max)
$randomString(len?)Alphanumeric string, len characters, 10 by default
$randomBoolean()true or false
$randomEmail()A plausible-looking email address
$randomFirstName()A first name
$randomLastName()A last name
$randomFullName()First and last, separated by a space
$randomUserName()A first.last123-shaped handle
$randomIp()An IPv4-shaped string
$randomUrl()An https:// URL
$randomPhoneNumber()A phone-number-shaped string
$randomCity()A city name
$randomCountryCode()An ISO-2 country code
$randomColor()A colour name
$randomPrice(min?, max?)A number with two decimal places, 1 to 1000 by default

The name, city, country and colour helpers read from small hand-written lists, roughly thirty entries each. They are there to make a fixture look like real data, not to model a population. If you need a specific locale or a large unique set, generate it in a script node and capture it.

Encoding and signing

HelperReturns
$base64(str)Base64 encoding of str
$base64decode(str)The inverse
$hash(str, algo?)Hex digest, algo defaults to sha256
$hmac(str, key, algo?)Hex HMAC digest keyed by key, algo defaults to sha256
$urlencode(str)encodeURIComponent(str)

algo is passed straight to Node’s crypto layer, so "sha1", "sha512" and the rest are all available if an API asks for one.

These exist because the sandbox a flow runs in does not otherwise hand you node:crypto. The sandbox is a typo boundary rather than a security boundary, so treat a flow file the way you would treat any code in your repository.

Every call generates a new value

There is no per-run caching. Two references to $uuid() in the same node produce two different ids, and that is usually what you want in a header. It is usually not what you want when the same value has to appear in a body and then in a later assertion.

To reuse one value, generate it once in a script node and read it back from variables:

version: "1.0"
name: Idempotent order

nodes:
  - id: mint_key
    type: script
    config:
      code: |
        variables.idempotencyKey = $uuid();
    next: place_order

  - id: place_order
    type: request
    config:
      method: POST
      url: ${{ variables.baseUrl }}/orders
      headers:
        Idempotency-Key: ${{ variables.idempotencyKey }}
      body: '{ "sku": "ABC-1" }'
    next: replay_order

  - id: replay_order
    type: request
    config:
      method: POST
      url: ${{ variables.baseUrl }}/orders
      headers:
        # The same key, so a correct API returns the first order rather than making a second.
        Idempotency-Key: ${{ variables.idempotencyKey }}
      body: '{ "sku": "ABC-1" }'
      capture:
        replayedId: response.body.id
    next: null

A capture does the same job for a value that comes back from the API rather than one you generated.

Not the flow-level variables: block. Values declared there are seeded as they are written and never go through ${{ }}, so key: ${{ $uuid() }} at the top of a file leaves variables.key holding that text verbatim. Interpolation happens where the value is used, not where it is declared. Use a script node, as above.

Signing a request body

$hmac is most useful in a hook, where it can sign whatever the request ended up being rather than what you typed:

hooks:
  before:
    script: |
      const ts = String($timestamp());
      request.headers["X-Timestamp"] = ts;
      request.headers["X-Signature"] = $hmac(ts + (request.body ?? ""), env.SIGNING_SECRET);

Hooks run before auth is resolved, so a signature written here covers the body that actually ships.

Coming from Postman

Postman’s most-used built-in dynamic variables map onto these helpers at import time: {{$guid}} becomes ${{ $uuid() }}, {{$randomEmail}} becomes ${{ $randomEmail() }}, and the same for $timestamp, $isoTimestamp, $randomInt, the four name helpers, $randomIP, $randomUrl, $randomPhoneNumber, $randomCity, $randomColor and $randomPrice.

They are converted into function calls rather than seeded as ordinary flow variables, which is what preserves the behaviour you had: each reference keeps regenerating, exactly as it did in Postman.

Postman has a much longer list than this. A {{$something}} with no counterpart is treated as an ordinary variable reference and comes through as ${{ variables.something }} with an empty value, so it shows up as an unset variable rather than disappearing. Point it at the nearest helper above, or at a script node.

Last updated

Looking for something else? All 37 articles are on one page in the Help Center.