> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devset.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Devset Expressions: Dynamic Values in Workflow Stages

> Expressions inject dynamic values — random data, timestamps, state references, and conditionals — into any value field in a stage definition.

Expressions are the mechanism Devset uses to make workflow payloads dynamic. Instead of hardcoding field values, you write expression objects that the engine resolves at runtime — generating random data, reading from the current event payload, pulling values out of workflow state, or computing results with built-in functions. Expressions are valid anywhere a value appears inside `set`, `state`, `emit`, `repeatWhile`, `repeatUntil`, and `key`.

## Value Types

Devset recognises four distinct value types. You can mix them freely within any stage.

### Plain Literal

A bare JSON value — string, number, boolean, or `null`. The engine uses it exactly as written.

```json theme={null}
{
  "set": {
    "status": "pending",
    "retryCount": 0,
    "isActive": true,
    "cancelledAt": null
  }
}
```

### Function Call (`$fn`)

An object with a `"$fn"` key whose value is a function expression string. The engine calls the named built-in function and substitutes the return value.

```json theme={null}
{
  "set": {
    "id": { "$fn": "uuid()" },
    "createdAt": { "$fn": "now()" },
    "amount": { "$fn": "int(100, 9999)" },
    "category": { "$fn": "choice(electronics, clothing, books)" }
  }
}
```

### Relative Field Reference (`$ref`)

An object with a `"$ref"` key that names a field in the **current stage's payload**. Use this to read a value that was set earlier in the same stage's `set` block, or carried over via `source: "previous-stage"`.

```json theme={null}
{
  "set": {
    "orderId": { "$fn": "uuid()" },
    "confirmationRef": { "$ref": "orderId" }
  }
}
```

<Note>
  `$ref` reads from the payload object being assembled by the current stage. To read from workflow-scoped state, use `$path` instead.
</Note>

### Absolute Path Reference (`$path`)

An object with a `"$path"` key containing a dot-notation path into the workflow state. Use this to read any value that was written by a previous stage's `state` block.

```json theme={null}
{
  "set": {
    "sequenceNumber": { "$path": "state.sequence.index" },
    "sessionId": { "$path": "state.session.id" }
  }
}
```

## Conditional Values (`when`)

The `when` form lets you express if/else logic inline. The engine evaluates the `when` expression; if it is truthy, the `value` is used, otherwise `default` is used.

```json theme={null}
{
  "set": {
    "priority": {
      "when": { "$fn": "gte(.amount, 1000)" },
      "value": "high",
      "default": "normal"
    }
  }
}
```

You can nest any expression type inside `value` and `default`:

```json theme={null}
{
  "set": {
    "discountCode": {
      "when": { "$fn": "eq(.membershipTier, premium)" },
      "value": { "$fn": "choice(SAVE10, SAVE20, SAVE30)" },
      "default": null
    }
  }
}
```

## Path References in Functions

Functions that take field arguments accept two path syntaxes:

| Syntax               | Reads from                               |
| -------------------- | ---------------------------------------- |
| `.fieldName`         | A field in the **current stage payload** |
| `state.dot.notation` | A path in the **workflow state** object  |

Examples:

```json theme={null}
{ "$fn": "gt(.unitPrice, 0)" }
```

Reads the `unitPrice` field from the current payload and checks whether it is greater than zero.

```json theme={null}
{ "$fn": "lt(state.batch.count, 50)" }
```

Reads `count` from `state.batch` and checks whether it is less than 50.

```json theme={null}
{ "$fn": "add(state.sequence.index, 1)" }
```

Adds 1 to the `index` value stored in `state.sequence`.

## Built-in Function Reference

| Function                            | Returns | Description                                             |
| ----------------------------------- | ------- | ------------------------------------------------------- |
| `uuid()`                            | string  | A random UUID v4                                        |
| `now()`                             | string  | Current timestamp as an ISO-8601 string                 |
| `nows()`                            | integer | Current timestamp in whole seconds since epoch          |
| `nowms()`                           | integer | Current timestamp in milliseconds since epoch           |
| `int(min, max)`                     | integer | A random integer between `min` and `max` inclusive      |
| `long(min, max)`                    | long    | A random long integer between `min` and `max` inclusive |
| `string()`                          | string  | A random UUID string                                    |
| `bit()`                             | boolean | A random `true` or `false`                              |
| `bool()`                            | boolean | A random `true` or `false`                              |
| `boolean()`                         | boolean | Alias for `bool()`                                      |
| `choice(v1, v2, ...)`               | any     | A uniformly random pick from the supplied values        |
| `choiceWeighted(v1:w1, v2:w2, ...)` | any     | A weighted random pick; weights are relative integers   |
| `add(a, b)`                         | number  | `a + b`                                                 |
| `sub(a, b)`                         | number  | `a - b`                                                 |
| `percent(value, total)`             | number  | `(value / total) * 100`                                 |
| `lt(a, b)`                          | boolean | `true` if `a < b`                                       |
| `lte(a, b)`                         | boolean | `true` if `a <= b`                                      |
| `gt(a, b)`                          | boolean | `true` if `a > b`                                       |
| `gte(a, b)`                         | boolean | `true` if `a >= b`                                      |
| `eq(a, b)`                          | boolean | `true` if `a == b`                                      |
| `neq(a, b)`                         | boolean | `true` if `a != b`                                      |

### Weighted choice example

```json theme={null}
{ "$fn": "choiceWeighted(completed:70, failed:20, pending:10)" }
```

This produces `"completed"` 70% of the time, `"failed"` 20% of the time, and `"pending"` 10% of the time.

## Expressions in Context

The following workflow fragment shows all four value types and a conditional used together in a realistic stage:

```json theme={null}
{
  "stage": "build-payment",
  "event": "payment.initiated",
  "source": "none",
  "set": {
    "paymentId": { "$fn": "uuid()" },
    "initiatedAt": { "$fn": "now()" },
    "amount": { "$fn": "int(500, 50000)" },
    "currency": { "$fn": "choice(USD, EUR, GBP)" },
    "method": { "$fn": "choiceWeighted(card:60, wallet:30, bank:10)" },
    "sequenceNumber": { "$path": "state.sequence.index" },
    "reference": { "$ref": "paymentId" },
    "requiresReview": {
      "when": { "$fn": "gte(.amount, 10000)" },
      "value": true,
      "default": false
    },
    "source": "api"
  },
  "state": {
    "sequence.index": { "$fn": "add(state.sequence.index, 1)" }
  },
  "emit": { "$fn": "gt(.amount, 0)" }
}
```

* `paymentId` and `initiatedAt` use `$fn` to generate random values at runtime.
* `amount`, `currency`, and `method` use `$fn` with weighted or range functions.
* `sequenceNumber` uses `$path` to read from workflow state.
* `reference` uses `$ref` to copy `paymentId` into a second field within the same payload.
* `requiresReview` uses a `when` conditional that checks the freshly assigned `amount`.
* `emit` uses `$fn` to skip dispatch if `amount` is zero.

<Tip>
  You can chain state mutations and field references across multiple stages. Write a value into state in one stage with `state`, then read it in a later stage with `{ "$path": "state.my.path" }`.
</Tip>
