Simulate Goal Bot
curl --request POST \
--url https://app.tuco.ai/api/goal-bots/{id}/simulate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"messages": [
{
"role": "<string>",
"content": "<string>"
}
]
}
'import requests
url = "https://app.tuco.ai/api/goal-bots/{id}/simulate"
payload = { "messages": [
{
"role": "<string>",
"content": "<string>"
}
] }
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({messages: [{role: '<string>', content: '<string>'}]})
};
fetch('https://app.tuco.ai/api/goal-bots/{id}/simulate', 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://app.tuco.ai/api/goal-bots/{id}/simulate",
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([
'messages' => [
[
'role' => '<string>',
'content' => '<string>'
]
]
]),
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://app.tuco.ai/api/goal-bots/{id}/simulate"
payload := strings.NewReader("{\n \"messages\": [\n {\n \"role\": \"<string>\",\n \"content\": \"<string>\"\n }\n ]\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://app.tuco.ai/api/goal-bots/{id}/simulate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"messages\": [\n {\n \"role\": \"<string>\",\n \"content\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.tuco.ai/api/goal-bots/{id}/simulate")
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 \"messages\": [\n {\n \"role\": \"<string>\",\n \"content\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"reply": "<string>",
"booked": true,
"turns": 123,
"toolCalls": [
{}
],
"collected": {}
}Goal Bots
Simulate Goal Bot
Run a goal bot against a transcript and see what it would say. Nothing is sent — no message, no draft, no notification. Shows which tools actually ran.
POST
/
api
/
goal-bots
/
{id}
/
simulate
Simulate Goal Bot
curl --request POST \
--url https://app.tuco.ai/api/goal-bots/{id}/simulate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"messages": [
{
"role": "<string>",
"content": "<string>"
}
]
}
'import requests
url = "https://app.tuco.ai/api/goal-bots/{id}/simulate"
payload = { "messages": [
{
"role": "<string>",
"content": "<string>"
}
] }
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({messages: [{role: '<string>', content: '<string>'}]})
};
fetch('https://app.tuco.ai/api/goal-bots/{id}/simulate', 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://app.tuco.ai/api/goal-bots/{id}/simulate",
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([
'messages' => [
[
'role' => '<string>',
'content' => '<string>'
]
]
]),
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://app.tuco.ai/api/goal-bots/{id}/simulate"
payload := strings.NewReader("{\n \"messages\": [\n {\n \"role\": \"<string>\",\n \"content\": \"<string>\"\n }\n ]\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://app.tuco.ai/api/goal-bots/{id}/simulate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"messages\": [\n {\n \"role\": \"<string>\",\n \"content\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.tuco.ai/api/goal-bots/{id}/simulate")
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 \"messages\": [\n {\n \"role\": \"<string>\",\n \"content\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"reply": "<string>",
"booked": true,
"turns": 123,
"toolCalls": [
{}
],
"collected": {}
}Nothing leaves the building. No message is sent, no draft is created, no notification fires
and no booking is made. This is the test chat.
toolCalls lists the tools
that actually ran, so a bot that only talks about checking a calendar is obvious immediately.
Also answers on POST /api/tasks/{id}/simulate.
Authentication
Pass your workspace API key as a Bearer token, or use a Clerk session token.Authorization: Bearer tuco_xxxxxxxxxxxxx
Path parameter
string
required
The bot id. A malformed id returns
400 invalid id.Request body
object[]
required
The transcript, oldest first. At least one message with non-empty content is required —
an empty array returns
400 messages are required.Only the last 25 messages are used, and each content is truncated to 500 characters.Example
curl -X POST "https://app.tuco.ai/api/goal-bots/6aa9813105bb2cbf5931ac1f/simulate" \
-H "Authorization: Bearer tuco_xxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{ "role": "user", "content": "hi, what do you do?" }
]
}'
Success (200 OK)
{
"ok": true,
"reply": "Hi! We offer a beautiful venue for weddings and events...",
"booked": false,
"turns": 0,
"toolCalls": [],
"collected": null
}
Response
boolean
true when the run completed.string
What the bot would have sent.
boolean
Whether the bot would have booked a slot. Nothing is actually booked.
number
Turns consumed in this run.
object[]
Tools the bot really invoked, each with its
name. An empty array means it called nothing.object | null
Values matched against
collectFields, or null when none were collected.Errors
| Status | When | Body |
|---|---|---|
400 | Malformed {id} | { "ok": false, "error": "invalid id" } |
400 | messages missing, empty, or all blank | { "ok": false, "error": "messages are required" } |
401 | Missing or invalid API key | { "ok": false, "error": "Unauthorized" } |
404 | No bot with that id in your workspace | { "ok": false, "error": "goal bot not found" } |
502 | The model run failed | { "ok": false, "error": "run failed" } |