curl --request POST \
--url https://api.lehar.ai/ca/api/v0/agent-test-suites \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <api-key>' \
--data '
{
"name": "Renewal objections",
"agent_id": "agent_sales_hi",
"scenarios": [
{
"label": "Price objection",
"instructions": "You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.",
"runs": 2,
"max_turns": 8
}
],
"config": {
"judges": [
"task_completion",
"safety"
],
"variables": {
"company": "Acme"
}
}
}
'import requests
url = "https://api.lehar.ai/ca/api/v0/agent-test-suites"
payload = {
"name": "Renewal objections",
"agent_id": "agent_sales_hi",
"scenarios": [
{
"label": "Price objection",
"instructions": "You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.",
"runs": 2,
"max_turns": 8
}
],
"config": {
"judges": ["task_completion", "safety"],
"variables": { "company": "Acme" }
}
}
headers = {
"X-API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'Renewal objections',
agent_id: 'agent_sales_hi',
scenarios: [
{
label: 'Price objection',
instructions: 'You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.',
runs: 2,
max_turns: 8
}
],
config: {judges: ['task_completion', 'safety'], variables: {company: 'Acme'}}
})
};
fetch('https://api.lehar.ai/ca/api/v0/agent-test-suites', 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.lehar.ai/ca/api/v0/agent-test-suites",
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([
'name' => 'Renewal objections',
'agent_id' => 'agent_sales_hi',
'scenarios' => [
[
'label' => 'Price objection',
'instructions' => 'You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.',
'runs' => 2,
'max_turns' => 8
]
],
'config' => [
'judges' => [
'task_completion',
'safety'
],
'variables' => [
'company' => 'Acme'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-KEY: <api-key>"
],
]);
$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.lehar.ai/ca/api/v0/agent-test-suites"
payload := strings.NewReader("{\n \"name\": \"Renewal objections\",\n \"agent_id\": \"agent_sales_hi\",\n \"scenarios\": [\n {\n \"label\": \"Price objection\",\n \"instructions\": \"You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.\",\n \"runs\": 2,\n \"max_turns\": 8\n }\n ],\n \"config\": {\n \"judges\": [\n \"task_completion\",\n \"safety\"\n ],\n \"variables\": {\n \"company\": \"Acme\"\n }\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-KEY", "<api-key>")
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.lehar.ai/ca/api/v0/agent-test-suites")
.header("X-API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Renewal objections\",\n \"agent_id\": \"agent_sales_hi\",\n \"scenarios\": [\n {\n \"label\": \"Price objection\",\n \"instructions\": \"You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.\",\n \"runs\": 2,\n \"max_turns\": 8\n }\n ],\n \"config\": {\n \"judges\": [\n \"task_completion\",\n \"safety\"\n ],\n \"variables\": {\n \"company\": \"Acme\"\n }\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.lehar.ai/ca/api/v0/agent-test-suites")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Renewal objections\",\n \"agent_id\": \"agent_sales_hi\",\n \"scenarios\": [\n {\n \"label\": \"Price objection\",\n \"instructions\": \"You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.\",\n \"runs\": 2,\n \"max_turns\": 8\n }\n ],\n \"config\": {\n \"judges\": [\n \"task_completion\",\n \"safety\"\n ],\n \"variables\": {\n \"company\": \"Acme\"\n }\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "test_suite_01hzy8x2m4q7r8s9t0v1w2x3y4",
"agent_id": "agent_sales_hi",
"name": "Renewal objections",
"scenarios": [
{
"label": "Price objection",
"instructions": "You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.",
"runs": 2,
"max_turns": 8
}
],
"config": {
"judges": [
"task_completion",
"safety"
],
"variables": {
"company": "Acme"
}
},
"created_by_user_id": "user_01EXAMPLE",
"created_at": "2026-05-22T10:00:00Z",
"updated_at": "2026-05-22T10:00:00Z"
}{
"error": {
"code": "invalid_request",
"message": "name is required"
}
}{
"error": {
"code": "conflict",
"message": "A test suite named 'Renewal objections' already exists in this workspace"
}
}Create test suite
Creates a reusable suite of persona scenarios for the Test Runner. agent_id is optional — a suite is a workspace-level object and the agent to test is chosen at run time; when provided it is validated against the workspace. A scenario’s persona may be left blank while drafting and is required only at run time. Scope sessions:write.
curl --request POST \
--url https://api.lehar.ai/ca/api/v0/agent-test-suites \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <api-key>' \
--data '
{
"name": "Renewal objections",
"agent_id": "agent_sales_hi",
"scenarios": [
{
"label": "Price objection",
"instructions": "You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.",
"runs": 2,
"max_turns": 8
}
],
"config": {
"judges": [
"task_completion",
"safety"
],
"variables": {
"company": "Acme"
}
}
}
'import requests
url = "https://api.lehar.ai/ca/api/v0/agent-test-suites"
payload = {
"name": "Renewal objections",
"agent_id": "agent_sales_hi",
"scenarios": [
{
"label": "Price objection",
"instructions": "You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.",
"runs": 2,
"max_turns": 8
}
],
"config": {
"judges": ["task_completion", "safety"],
"variables": { "company": "Acme" }
}
}
headers = {
"X-API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'Renewal objections',
agent_id: 'agent_sales_hi',
scenarios: [
{
label: 'Price objection',
instructions: 'You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.',
runs: 2,
max_turns: 8
}
],
config: {judges: ['task_completion', 'safety'], variables: {company: 'Acme'}}
})
};
fetch('https://api.lehar.ai/ca/api/v0/agent-test-suites', 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.lehar.ai/ca/api/v0/agent-test-suites",
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([
'name' => 'Renewal objections',
'agent_id' => 'agent_sales_hi',
'scenarios' => [
[
'label' => 'Price objection',
'instructions' => 'You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.',
'runs' => 2,
'max_turns' => 8
]
],
'config' => [
'judges' => [
'task_completion',
'safety'
],
'variables' => [
'company' => 'Acme'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-KEY: <api-key>"
],
]);
$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.lehar.ai/ca/api/v0/agent-test-suites"
payload := strings.NewReader("{\n \"name\": \"Renewal objections\",\n \"agent_id\": \"agent_sales_hi\",\n \"scenarios\": [\n {\n \"label\": \"Price objection\",\n \"instructions\": \"You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.\",\n \"runs\": 2,\n \"max_turns\": 8\n }\n ],\n \"config\": {\n \"judges\": [\n \"task_completion\",\n \"safety\"\n ],\n \"variables\": {\n \"company\": \"Acme\"\n }\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-KEY", "<api-key>")
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.lehar.ai/ca/api/v0/agent-test-suites")
.header("X-API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Renewal objections\",\n \"agent_id\": \"agent_sales_hi\",\n \"scenarios\": [\n {\n \"label\": \"Price objection\",\n \"instructions\": \"You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.\",\n \"runs\": 2,\n \"max_turns\": 8\n }\n ],\n \"config\": {\n \"judges\": [\n \"task_completion\",\n \"safety\"\n ],\n \"variables\": {\n \"company\": \"Acme\"\n }\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.lehar.ai/ca/api/v0/agent-test-suites")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Renewal objections\",\n \"agent_id\": \"agent_sales_hi\",\n \"scenarios\": [\n {\n \"label\": \"Price objection\",\n \"instructions\": \"You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.\",\n \"runs\": 2,\n \"max_turns\": 8\n }\n ],\n \"config\": {\n \"judges\": [\n \"task_completion\",\n \"safety\"\n ],\n \"variables\": {\n \"company\": \"Acme\"\n }\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "test_suite_01hzy8x2m4q7r8s9t0v1w2x3y4",
"agent_id": "agent_sales_hi",
"name": "Renewal objections",
"scenarios": [
{
"label": "Price objection",
"instructions": "You are a budget-conscious customer who thinks the renewal price is too high. Push back on cost before agreeing to anything.",
"runs": 2,
"max_turns": 8
}
],
"config": {
"judges": [
"task_completion",
"safety"
],
"variables": {
"company": "Acme"
}
},
"created_by_user_id": "user_01EXAMPLE",
"created_at": "2026-05-22T10:00:00Z",
"updated_at": "2026-05-22T10:00:00Z"
}{
"error": {
"code": "invalid_request",
"message": "name is required"
}
}{
"error": {
"code": "conflict",
"message": "A test suite named 'Renewal objections' already exists in this workspace"
}
}Authorizations
Body
Suite name, unique within the workspace (max 200 chars).
200Optional agent to associate as an origin hint. Validated against the workspace when set.
Persona scenarios (max 100). Most fields are passed through to the Test Runner; only runs, max_turns, and variables are validated here.
100Show child attributes
Show child attributes
Suite-level defaults. variables (validated) seeds {{variable}} values for every scenario; judges, runs, max_turns, simulator_model, judge_model act as defaults a scenario can override; max_parallel (1-8, default 3) bounds scenario fan-out.
Show child attributes
Show child attributes
Response
Created test suite

