Trellis v0.4+ supports a Stateless HTTP Server Mode, allowing you to integrate the state machine engine into web applications, serverless functions, or AI agents.
This guide uses the built-in examples/tour flow to demonstrate how to run the server and interact with the API via Swagger UI.
You can start the server using the trellis serve command. Provide the directory of the flow you want to serve.
# Serve the 'tour' example on port 8080
go run ./cmd/trellis serve --dir ./examples/tour --port 8080
Or using the Makefile (development):
make serve-tour
The server exposes a /events endpoint for real-time updates using Server-Sent Events (SSE). It supports two modes:
?session_id=...): Used by frontends to receive granular state updates (Deltas).sequenceDiagram
participant Client
participant Server
Client->>Server: GET /events?session_id=123
Server-->>Client: event: ping (connected)
Client->>Server: POST /navigate (Input="next")
Server->>Server: Calculate State Diff
Server-->>Client: data: {"context": {"foo": "bar"}}
Client->>Client: Patch Local State
The SSE data payload is a JSON object representing only what changed:
{
"session_id": "123",
"current_node_id": "step_2",
"status": "active",
"context": {
"points": 10
},
"history": {
"appended": ["step_2"]
}
}
You can filter which fields to watch using the watch parameter:
curl -N "http://localhost:8080/events?session_id=123&watch=context,status"
Once the server is running, open your browser to:
👉 http://localhost:8080/swagger
You will see the interactive API documentation.
GET /health -> {"status": "ok"}GET /info -> Returns version metadata using the Separation of Concerns strategy:
version: The Trellis software version (e.g. 0.3.3)api_version: The OpenAPI Contract version (e.g. 0.1.0)The tour flow starts at the start node. Since the server is stateless, you (the client) are responsible for holding the state object and passing it back to the server for each step.
First, we ask the engine to render the view for the entry point (start).
POST /renderPayload:
{
"current_node_id": "start"
}
Response (View):
The server returns actions (e.g., text to display) and terminal: false.
{
"actions": [
{
"type": "RENDER_CONTENT",
"payload": "# Welcome to Trellis Tour\n\nThis is a simple interactive tour..."
}
],
"terminal": false
}
The text asks us to choose “Inputs” or “Choice”. Let’s type “Inputs”. We must send our Current State (which we constructed from the previous step: we are at start) and our Input.
POST /navigatePayload:
{
"state": {
"current_node_id": "start",
"memory": {},
"history": ["start"]
},
"input": "Inputs"
}
Response (New State): The server calculates the transition and returns the new state object.
{
"current_node_id": "inputs",
"history": ["start", "inputs"],
"memory": {},
"terminated": false
}
Now that we are at the inputs node, we call render again with this new state to see what happens next.
POST /renderPayload:
{
"current_node_id": "inputs",
"history": ["start", "inputs"],
"memory": {}
}
The loop is always: