Return dynamic data from a mock API: UUIDs, timestamps & random values
Static mock responses get boring fast. Dynamic tokens let your webhook return a fresh UUID, the current timestamp, a random value, or an echo of the incoming request — computed at the moment each request is served.
Static mock responses get boring fast
A published Yellorn webhook returns whatever payload you stored — and by default that's a fixed string, the same bytes on every request. That's fine for a quick stub, but real clients want responses that vary: a fresh id per call, the current timestamp, a random amount, a status that flips between active and pending.
Dynamic tokens solve this. Wrap a small expression in {{ … }} anywhere in the payload and Yellorn evaluates it at the moment the request is served — so the response is computed live, per hit, without you redeploying anything.
A 30-second example
Store this as a webhook payload:
{
"id": "{{ uuid4() }}",
"created_at": "{{ now() }}",
"attempt": {{ randint(1, 5) }},
"status": "{{ choice("active", "pending", "failed") }}",
"caller": "{{ request.ip }}"
}Every GET against the slug URL returns something like:
{
"id": "0b5d1e94-2c1a-4f3e-9b8a-7d6c5e4f3a21",
"created_at": "2026-06-25T01:00:50.842000",
"attempt": 3,
"status": "pending",
"caller": "203.0.113.7"
}…with a different id, attempt, status, and caller on the next request. The expression language is intentionally Python-flavoured — now(), timedelta(days=1), randint(1, 9) — so it reads the way a Python developer expects.
Where tokens work
- Webhook response body (the
/webhook/<slug>portal) — and these can echo the inbound request with{{ request.* }}. - Request Sender (
/sender/<id>) — the request body, the URL, and header values. There's no inbound request to echo here, so therequest.*tokens are hidden on this surface.
As the owner editing through the portal you always see the raw template ({{ now() }}), never a one-off rendered value — rendering only happens for public callers hitting the URL.
The token reference
Date & time
{{ now() }}— ISO-8601 timestamp of the moment the request is served.{{ now("%Y-%m-%d %H:%M:%S") }}— format the current time with a Pythonstrftimepattern.{{ today() }},{{ timestamp() }}(Unix seconds),{{ timestamp_ms() }}(milliseconds).- Relative time —
{{ now() + timedelta(days=1) }}(tomorrow),{{ now() - timedelta(hours=2) }}(two hours ago).timedeltatakesweeks,days,hours,minutes,seconds,milliseconds. - Datetime attributes & methods —
{{ now().year }},{{ now().date() }},{{ now().isoformat() }}.
Identifiers
{{ uuid4() }}— a random RFC-4122 v4 UUID.{{ objectid() }}— a 24-char hex Mongo ObjectId (timestamp + random).{{ snowflake() }}— a 64-bit-style numeric, timestamp-ordered id.
Random values
{{ randint(1, 100) }}— integer in the inclusive range.{{ randfloat(0, 1) }}— float in the half-open range.{{ randstr(16) }}/{{ randhex(32) }}— random alphanumeric / hex strings.{{ randbool() }}—true/falseat 50/50.{{ choice("a", "b", "c") }}— one of the arguments, picked at random.
Echo the request (webhook only)
{{ request.method }},{{ request.ip }},{{ request.url }},{{ request.path }},{{ request.body }}.{{ request.header("User-Agent") }}— a header value (case-insensitive).{{ request.query("id") }}— a URL query-string parameter.
Strings, numbers, JSON
{{ int(x) }}, {{ str(x) }}, {{ upper(s) }}, {{ round(n, 2) }}, {{ min(…) }} / {{ max(…) }}, and {{ jsonstr(x) }} (JSON-encode a value with quotes + escapes).
Keeping the JSON valid
The renderer substitutes a token's text form directly into the body, so what you put around the token decides the JSON shape:
// String value — wrap the token in quotes:
{ "at": "{{ now() }}" } → { "at": "2026-06-25T01:00:50.842000" }
// Bare number — no quotes:
{ "n": {{ randint(1, 9) }} } → { "n": 4 }
// Bare boolean:
{ "ok": {{ randbool() }} } → { "ok": true }When you echo untrusted request data into a JSON string, wrap it in jsonstr(...) so quotes, backslashes, and newlines are escaped and the document stays valid. jsonstr adds the surrounding quotes itself — don't also wrap it in literal "…":
{ "ua": {{ jsonstr(request.header("User-Agent")) }} }
// → { "ua": "Mozilla/5.0 (\"quoted\" UA)" }Discover, preview, and validate
- Insert token picker — a searchable, categorised popover above the body field. Each row shows the snippet, a one-line description, and a live sample preview computed by the real engine, so the preview can never drift from reality.
- In-editor autocomplete — start typing inside a
{{ … }}token and a popover offers every builtin with its signature and a live sample value, inserting snippets with tab-stops so you can fill arguments by pressing Tab. Ordinary JSON / YAML editing outside a token is never interrupted. - Preview toggle — when the body contains a token, flip to Preview to render the whole body with deterministic sample values and see the response shape before publishing.
- Two-phase validation — before a tokenized body is stored, Yellorn checks every token parses and that the rendered result still passes the format validator. A malformed token (
{{ randint( }}) is rejected with a friendly message; the template is never stored broken.
A token that throws at render time keeps its literal {{ … }} in the output and is reported — it's always visible, never silently blanked, and one bad token can never break the public hot path.
Related
Where to go next
Try a fix in the editor or browse more articles.