User Journeys
Journeys are re-runnable prompts. Instead of creating a new agent from scratch every time, you define a journey once and run it repeatedly. Each run creates a new agent linked back to the journey, giving you a history of results for the same test over time.
Overview
A journey is an abstraction on top of the agent API. Where creating an agent is a one-off operation (one prompt, one run, one result), a journey lets you define a prompt once and run it as many times as you want. Each run creates a new agent that is linked back to the journey, so you build up a history of results for the same test over time.
This is the core mechanism for tracking the health of your application. If you have a journey called "checkout flow" and you run it after every deploy, you get a timeline of pass/fail results that tells you exactly when something broke and when it was fixed. The project health board aggregates this across all journeys in a project.
When you run a journey, the system resolves the persona and role (cascading from the run parameters, to the journey defaults, to the project defaults), renders the prompt, creates an agent, and queues it for execution.
Checkpoints
Checkpoints are a JSON array of named milestones that you expect the agent to reach during execution. Each checkpoint has a name and a description.
[ {"name": "login", "description": "Agent logged in successfully"}, {"name": "add_to_cart", "description": "Added item to cart"}, {"name": "checkout", "description": "Completed the checkout flow"} ]Checkpoints are not evaluated during execution. The agent runs its agentic loop normally without any awareness of the checkpoints. After the agent completes, the full conversation trace is analyzed and each checkpoint is evaluated based on the evidence in the conversation. The result is a list of checkpoints that were hit and a list that were missed.
This design means checkpoints are purely observational. They do not alter the agent's behavior or cause early termination. They give you structured visibility into how far through a multi-step flow the agent got, which is especially useful when a journey fails partway through.
Success determination
Whether a journey run succeeded or failed is determined by a two-step process. First, the agent itself reports its own assessment when it finishes (success or failure with a summary). Second, a server-side verification step independently analyzes the full conversation trace and produces its own verdict.
If the agent and the server-side verification disagree, the server-side verdict wins. This means an agent cannot incorrectly report success if the evidence in the conversation shows otherwise, and vice versa. The final outcome is what you see in the API response and on the health board.
Persona and role resolution
When a journey is run, the persona and role are resolved using a cascading fallback. The system first checks if a persona or role was specified in the run parameters. If not, it falls back to the defaults set on the journey itself. If the journey has no defaults, it uses the project-level defaults.
This lets you set sensible defaults on the journey or project and override them on individual runs when needed. For example, you might have a journey that normally runs with a "Functional QA" role but occasionally run it with a "Power User" role to test a different interaction style.
Create a new user journey
Parameters
| Parameter | Type | In | Required | Description |
|---|---|---|---|---|
project_id | uuid | body | Yes | ID of the project this user journey belongs to |
name | string | body | Yes | User journey name |
description | string | body | No | User journey description |
prompt_template | string | body | Yes | Go text/template prompt with {{.Variables}} |
entry_url | string | body | No | Starting URL for the user journey |
checkpoints | json | body | No | Checkpoint definitions for progress tracking |
success_criteria | json | body | No | Criteria to determine user journey success |
source | string | body | No | Origin of the user journey (e.g. 'discovery', 'manual') |
importance | string | body | No | User journey importance level (e.g. 'critical', 'high', 'medium', 'low') |
file_paths | string[] | body | No | Paths of tenant files to copy into the test run workspace when running this user journey |
Status Codes
| Code | Description |
|---|---|
201 | Journey created |
400 | Validation error |
401 | Unauthorized |
Response Body
{
"id": "770e8400-e29b-41d4-a716-446655440000",
"tenant_id": "550e8400-e29b-41d4-a716-446655440000",
"project_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Checkout Flow",
"description": "",
"prompt_template": "Navigate to {{.EntryURL}} and complete checkout",
"entry_url": "https://example.com",
"checkpoints": [],
"success_criteria": null,
"file_paths": [],
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}/api/v1/journeysList user journeys
Parameters
| Parameter | Type | In | Required | Description |
|---|---|---|---|---|
limit | integer | query | No | Number of results to return (default: 20) |
cursor | uuid | query | No | Cursor for pagination |
project_id | uuid | query | No | Filter by project ID (alias: project) |
Status Codes
| Code | Description |
|---|---|
200 | OK |
401 | Unauthorized |
Response Body
{
"journeys": [
{
"id": "770e8400-e29b-41d4-a716-446655440000",
"tenant_id": "550e8400-e29b-41d4-a716-446655440000",
"project_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Checkout Flow",
"description": "",
"prompt_template": "Navigate to {{.EntryURL}} and complete checkout",
"entry_url": "https://example.com",
"checkpoints": [],
"success_criteria": null,
"file_paths": [],
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}
],
"next_cursor": "880e8400-e29b-41d4-a716-446655440000"
}/api/v1/journeysGet user journey by ID
Parameters
| Parameter | Type | In | Required | Description |
|---|---|---|---|---|
id | uuid | path | Yes | User journey ID |
Status Codes
| Code | Description |
|---|---|
200 | OK |
400 | Invalid UUID |
401 | Unauthorized |
404 | Journey not found |
Response Body
{
"id": "770e8400-e29b-41d4-a716-446655440000",
"tenant_id": "550e8400-e29b-41d4-a716-446655440000",
"project_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Checkout Flow",
"description": "",
"prompt_template": "Navigate to {{.EntryURL}} and complete checkout",
"entry_url": "https://example.com",
"checkpoints": [],
"success_criteria": null,
"file_paths": [],
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}/api/v1/journeys/{id}Update a user journey
Parameters
| Parameter | Type | In | Required | Description |
|---|---|---|---|---|
id | uuid | path | Yes | User journey ID |
name | string | body | No | User journey name |
description | string | body | No | User journey description |
prompt_template | string | body | No | Go text/template prompt |
entry_url | string | body | No | Starting URL |
checkpoints | json | body | No | Checkpoint definitions |
success_criteria | json | body | No | Success criteria |
source | string | body | No | Origin of the user journey |
importance | string | body | No | User journey importance level |
file_paths | string[] | body | No | Paths of tenant files to copy into the test run workspace when running this user journey |
Status Codes
| Code | Description |
|---|---|
200 | Journey updated |
400 | Validation error |
401 | Unauthorized |
404 | Journey not found |
Response Body
{
"id": "770e8400-e29b-41d4-a716-446655440000",
"tenant_id": "550e8400-e29b-41d4-a716-446655440000",
"project_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Checkout Flow",
"description": "",
"prompt_template": "Navigate to {{.EntryURL}} and complete checkout",
"entry_url": "https://example.com",
"checkpoints": [],
"success_criteria": null,
"file_paths": [],
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}/api/v1/journeys/{id}Delete a user journey
Parameters
| Parameter | Type | In | Required | Description |
|---|---|---|---|---|
id | uuid | path | Yes | User journey ID |
Status Codes
| Code | Description |
|---|---|
204 | Journey deleted |
400 | Invalid UUID |
401 | Unauthorized |
404 | Journey not found |
/api/v1/journeys/{id}Run a user journey
Creates a new test run from the user journey template, rendering the prompt with the supplied variables. Files attached to the user journey are automatically copied into the test run workspace.
Parameters
| Parameter | Type | In | Required | Description |
|---|---|---|---|---|
id | uuid | path | Yes | User journey ID |
persona_id | uuid | body | No | Persona for browser session (cookies, credentials, email) |
role_id | uuid | body | No | Role for prompt injection context |
variables | object | body | No | Template variables to inject into prompt_template |
model | string | body | No | LLM model override for this run |
max_iterations | integer | body | No | Max iterations override for this run |
browser_type | string | body | No | Browser to use (default: chrome) |
record_video | boolean | body | No | Save recordings for all runs. When false (default), only failed runs are saved. (default: false) |
Status Codes
| Code | Description |
|---|---|
201 | Agent created from journey |
400 | Validation error or template rendering failed |
401 | Unauthorized |
404 | Journey not found |
Response Body
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "660e8400-e29b-41d4-a716-446655440000",
"project_id": "770e8400-e29b-41d4-a716-446655440000",
"persona_id": "880e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"prompt": "Navigate to https://example.com and complete checkout",
"model": "gemini-2.5-flash",
"browser_type": "chrome",
"record_video": false,
"agent_type": "direct",
"messages": [],
"iteration": 0,
"max_iterations": 115,
"tokens_used": 0,
"journey_id": "990e8400-e29b-41d4-a716-446655440000",
"source": "api",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}/api/v1/journeys/{id}/run