Test a flow (hermetic simulator)
curl --request POST \
--url https://api.goyappr.com/agents/{id}/flow/test \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"transcript": [
{
"text": "<string>"
}
],
"mock_tool_results": {}
}
'import requests
url = "https://api.goyappr.com/agents/{id}/flow/test"
payload = {
"transcript": [{ "text": "<string>" }],
"mock_tool_results": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({transcript: [{text: '<string>'}], mock_tool_results: {}})
};
fetch('https://api.goyappr.com/agents/{id}/flow/test', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.goyappr.com/agents/{id}/flow/test",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'transcript' => [
[
'text' => '<string>'
]
],
'mock_tool_results' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.goyappr.com/agents/{id}/flow/test"
payload := strings.NewReader("{\n \"transcript\": [\n {\n \"text\": \"<string>\"\n }\n ],\n \"mock_tool_results\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.goyappr.com/agents/{id}/flow/test")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"transcript\": [\n {\n \"text\": \"<string>\"\n }\n ],\n \"mock_tool_results\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.goyappr.com/agents/{id}/flow/test")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"transcript\": [\n {\n \"text\": \"<string>\"\n }\n ],\n \"mock_tool_results\": {}\n}"
response = http.request(request)
puts response.read_body{
"trace": [
{
"step_id": "<string>",
"kind": "enter",
"decision": "<string>",
"reason": "<string>",
"data": {}
}
],
"named_results": {},
"slot_values": {},
"ended_at_step_id": "<string>"
}Flow Agents
Test a Flow
Walks a flow graph against a synthetic transcript without dispatching real tools or writing a call_logs row. Conversation-node transitions are picked by a deterministic keyword heuristic — for true eval-LLM-driven simulation use the in-app test panel. Tool-call nodes consume mocked results from the request body. Useful for CI smoke tests and skill verification before going live.
POST
/
agents
/
{id}
/
flow
/
test
Test a flow (hermetic simulator)
curl --request POST \
--url https://api.goyappr.com/agents/{id}/flow/test \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"transcript": [
{
"text": "<string>"
}
],
"mock_tool_results": {}
}
'import requests
url = "https://api.goyappr.com/agents/{id}/flow/test"
payload = {
"transcript": [{ "text": "<string>" }],
"mock_tool_results": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({transcript: [{text: '<string>'}], mock_tool_results: {}})
};
fetch('https://api.goyappr.com/agents/{id}/flow/test', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.goyappr.com/agents/{id}/flow/test",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'transcript' => [
[
'text' => '<string>'
]
],
'mock_tool_results' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.goyappr.com/agents/{id}/flow/test"
payload := strings.NewReader("{\n \"transcript\": [\n {\n \"text\": \"<string>\"\n }\n ],\n \"mock_tool_results\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.goyappr.com/agents/{id}/flow/test")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"transcript\": [\n {\n \"text\": \"<string>\"\n }\n ],\n \"mock_tool_results\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.goyappr.com/agents/{id}/flow/test")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"transcript\": [\n {\n \"text\": \"<string>\"\n }\n ],\n \"mock_tool_results\": {}\n}"
response = http.request(request)
puts response.read_body{
"trace": [
{
"step_id": "<string>",
"kind": "enter",
"decision": "<string>",
"reason": "<string>",
"data": {}
}
],
"named_results": {},
"slot_values": {},
"ended_at_step_id": "<string>"
}Hermetic flow simulator. Walks a
Optionally include
flow_config graph against a synthetic transcript without dispatching real tools, writing a call_logs row, or placing a real call. Useful for CI smoke tests, skill verification, and pre-deploy sanity checks.
Required scope: flows:test (separate from agents:update because flow tests can spend money on eval LLMs and external APIs in richer test modes).
What this endpoint does
- Loads the agent’s saved
flow_config(or uses the override you supply in the request body). - For each conversation node: consumes the next
role: "user"turn from your transcript and picks a transition by deterministic keyword overlap with each transition’s label/description. Misses route to “stay”. - For each tool-call node: looks up
mock_tool_results[step_id]. Iferroris set, takes theerrortransition. Otherwise takessuccess, with custom branches evaluated in declaration order via thejsonpath/equalsrule. - At an end node (or transfer, or post-end webhook/structured_output), the walk terminates and returns the full trace.
What this endpoint does NOT do
- It does not call the eval LLM. The deterministic heuristic is good enough for unit tests of branching topology, not for testing prompt quality. For eval-LLM-driven simulation use the in-app Flow Test panel (which runs a real bot pipeline against a WebRTC web call).
- It does not dispatch tools. Mock every tool-call node you reach via
mock_tool_results. - It does not write
call_logsor fire webhooks.
Body
{
"transcript": [
{ "role": "user", "text": "I want to book for next Wednesday at 2pm" }
],
"mock_tool_results": {
"check-availability-step": {
"result": { "status": "available", "slot_id": "abc123" }
}
}
}
flow_config in the body to test an unsaved draft instead of the agent’s stored graph.
Response
{
"trace": [
{ "step_id": "start-1", "kind": "enter" },
{ "step_id": "start-1", "kind": "auto_advance", "decision": "ask-date-1" },
{ "step_id": "ask-date-1", "kind": "enter" },
{ "step_id": "ask-date-1", "kind": "eval", "decision": "tx-confirm", "reason": "heuristic match (overlap_score=2)" },
{ "step_id": "check-availability-step", "kind": "enter" },
{ "step_id": "check-availability-step", "kind": "tool_mock", "decision": "confirm-step", "reason": "success", "data": { "status": "available", "slot_id": "abc123" } },
{ "step_id": "end-1", "kind": "enter" },
{ "step_id": "end-1", "kind": "end" }
],
"named_results": { "Check Availability": { "status": "available", "slot_id": "abc123" } },
"slot_values": {},
"ended_at_step_id": "end-1"
}
Authorizations
Your Yappr API key (e.g. ypr_live_...). Generate one in the dashboard under Settings → API Keys.
Path Parameters
Body
application/json