> ## 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.

# Workflow DSL Format: Complete Reference Guide

> Complete reference for Devset's JSON workflow DSL. Define brokers, payloads, execution counts, and pipeline stages for Kafka and RabbitMQ testing.

A Devset workflow is a single JSON document that describes everything needed to drive an event-driven test scenario: which broker to target, how many times to run, what state to track, and the ordered sequence of stages that produce messages. You author workflows by hand or generate them from templates, then hand them to the Devset engine or Playground to execute.

## Full Example

The following workflow publishes fifty `order-created` events to a Kafka topic, accumulating a running total and message count in shared state between each execution.

```json theme={null}
{
  "id": "order-flow",
  "messageType": "kafka",
  "contentType": "application/json",
  "producerName": "local-kafka",
  "topic": "orders.events",
  "schemaId": "order-created",
  "executions": 50,
  "state": {
    "order": { "total": 0, "count": 0 }
  },
  "pipeline": [
    {
      "stage": "order-created",
      "event": "order-created",
      "emit": true,
      "set": {
        "id": { "$fn": "uuid()" },
        "amount": { "$fn": "int(10, 500)" },
        "status": "PENDING"
      },
      "state": {
        "order.total": { "$fn": "add(state.order.total, .amount)" },
        "order.count": { "$fn": "add(state.order.count, 1)" }
      }
    }
  ]
}
```

## Top-Level Fields

<ParamField path="id" type="string" required>
  A unique identifier for this workflow. Used in logs, metrics, and the Playground UI to distinguish one workflow from another. Keep it short and descriptive — for example `"order-flow"` or `"user-signup-v2"`.
</ParamField>

<ParamField path="messageType" type="&#x22;kafka&#x22; | &#x22;rabbit&#x22;" default="&#x22;kafka&#x22;">
  The broker type this workflow targets. Set to `"kafka"` for Apache Kafka and `"rabbit"` for RabbitMQ. This controls which routing fields are relevant (`topic` for Kafka; `exchange` and `routingKey` for RabbitMQ).
</ParamField>

<ParamField path="contentType" type="&#x22;application/json&#x22; | &#x22;application/x-protobuf&#x22;" default="&#x22;application/json&#x22;">
  The payload encoding format. Use `"application/json"` for human-readable JSON payloads. Use `"application/x-protobuf"` for binary Protobuf payloads — when using Protobuf you will also need to configure the [`wireFormat`](/reference/wire-format) block on each stage.
</ParamField>

<ParamField path="producerName" type="string" required>
  The name of a configured broker connector to use when dispatching messages. This must match a connector you have registered in your Devset environment.
</ParamField>

<ParamField path="topic" type="string">
  The Kafka topic to publish messages to. Required when `messageType` is `"kafka"`. You can override this per-stage if your workflow publishes to multiple topics.
</ParamField>

<ParamField path="exchange" type="string">
  The RabbitMQ exchange to publish messages to. Used only when `messageType` is `"rabbit"`.
</ParamField>

<ParamField path="routingKey" type="string">
  The RabbitMQ routing key attached to every outbound message. Used only when `messageType` is `"rabbit"`.
</ParamField>

<ParamField path="schemaId" type="string">
  The default schema identifier applied to every stage in the pipeline. Individual stages can override this with their own `schemaId` field. The schema ID is used for payload validation and, when using Protobuf, for resolving the type descriptor.
</ParamField>

<ParamField path="executions" type="integer" default="1">
  The number of times Devset runs the entire pipeline from start to finish. Setting this to `50` causes every stage in the pipeline to execute fifty times in order, with workflow state persisting and accumulating across all executions. See [Execution Model](#execution-model) below for details.
</ParamField>

<ParamField path="state" type="object" default="{}">
  The initial mutable state object shared across all stages and all executions. Declare your starting values here — for example counters, accumulators, or lookup results. Stages mutate state via their own `state` block using [DSL functions](/reference/functions).
</ParamField>

<ParamField path="pipeline" type="Stage[]" required>
  An ordered array of [Stage](/reference/stage-fields) objects. Devset executes stages in the order they appear, passing context from one stage to the next. At least one stage is required.
</ParamField>

## Execution Model

When you set `executions` to a value greater than `1`, Devset runs the entire `pipeline` array that many times in sequence. Think of one execution as a single "scenario run" — for example, one full order lifecycle.

State declared at the top level persists across executions. This means you can accumulate totals, counts, or other cross-run statistics. In the example above, `order.total` and `order.count` grow with every execution because each stage's `state` block adds to the running values rather than replacing them.

```json theme={null}
{
  "executions": 10,
  "state": {
    "sentCount": 0
  },
  "pipeline": [
    {
      "stage": "ping",
      "emit": true,
      "set": { "msg": "hello" },
      "state": {
        "sentCount": { "$fn": "add(state.sentCount, 1)" }
      }
    }
  ]
}
```

After all ten executions complete, `state.sentCount` equals `10`.

<Note>
  State is reset to its initial values only when you start a new workflow run. Within a single run, all executions share and mutate the same state object.
</Note>

## Kafka vs RabbitMQ

The top-level routing fields differ depending on your `messageType`.

**Kafka** workflows use `topic` to identify the destination:

```json theme={null}
{
  "id": "kafka-example",
  "messageType": "kafka",
  "topic": "orders.events"
}
```

**RabbitMQ** workflows use `exchange` and `routingKey` instead:

```json theme={null}
{
  "id": "rabbit-example",
  "messageType": "rabbit",
  "exchange": "orders.exchange",
  "routingKey": "order.created"
}
```

<Tip>
  You can omit `exchange` if you are publishing to the RabbitMQ default exchange, and rely purely on `routingKey` for routing.
</Tip>

## Related Pages

* [Stage Fields](/reference/stage-fields) — full reference for every field a pipeline stage supports, including `set`, `state`, `repeat`, and `query`.
* [Functions](/reference/functions) — complete catalog of built-in `$fn` expressions you can use in stage field values.
* [Wire Format](/reference/wire-format) — Protobuf binary framing configuration for Confluent Schema Registry compatibility.
