curl --request POST \
--url https://api.sidenet.ai/v1/experiments \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'X-User-Id: <x-user-id>' \
--data '
{
"agentId": "6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10",
"datasetId": "ds_orders_v3",
"scorers": [
"completeness",
"tone"
],
"async": false,
"name": "Refund tone — August",
"description": "Checks the refund answers after the new brand-voice block",
"metadata": {
"ticket": "ENG-412"
},
"version": 3,
"maxConcurrency": 5,
"itemTimeout": 60000,
"maxRetries": 2,
"variables": {
"locale": "English",
"instance": {
"store_count": 12
}
},
"strictVariables": true
}
'import requests
url = "https://api.sidenet.ai/v1/experiments"
payload = {
"agentId": "6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10",
"datasetId": "ds_orders_v3",
"scorers": ["completeness", "tone"],
"async": False,
"name": "Refund tone — August",
"description": "Checks the refund answers after the new brand-voice block",
"metadata": { "ticket": "ENG-412" },
"version": 3,
"maxConcurrency": 5,
"itemTimeout": 60000,
"maxRetries": 2,
"variables": {
"locale": "English",
"instance": { "store_count": 12 }
},
"strictVariables": True
}
headers = {
"X-User-Id": "<x-user-id>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-User-Id': '<x-user-id>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
agentId: '6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10',
datasetId: 'ds_orders_v3',
scorers: ['completeness', 'tone'],
async: false,
name: 'Refund tone — August',
description: 'Checks the refund answers after the new brand-voice block',
metadata: {ticket: 'ENG-412'},
version: 3,
maxConcurrency: 5,
itemTimeout: 60000,
maxRetries: 2,
variables: {locale: 'English', instance: {store_count: 12}},
strictVariables: true
})
};
fetch('https://api.sidenet.ai/v1/experiments', 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.sidenet.ai/v1/experiments",
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([
'agentId' => '6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10',
'datasetId' => 'ds_orders_v3',
'scorers' => [
'completeness',
'tone'
],
'async' => false,
'name' => 'Refund tone — August',
'description' => 'Checks the refund answers after the new brand-voice block',
'metadata' => [
'ticket' => 'ENG-412'
],
'version' => 3,
'maxConcurrency' => 5,
'itemTimeout' => 60000,
'maxRetries' => 2,
'variables' => [
'locale' => 'English',
'instance' => [
'store_count' => 12
]
],
'strictVariables' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"X-User-Id: <x-user-id>"
],
]);
$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.sidenet.ai/v1/experiments"
payload := strings.NewReader("{\n \"agentId\": \"6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10\",\n \"datasetId\": \"ds_orders_v3\",\n \"scorers\": [\n \"completeness\",\n \"tone\"\n ],\n \"async\": false,\n \"name\": \"Refund tone — August\",\n \"description\": \"Checks the refund answers after the new brand-voice block\",\n \"metadata\": {\n \"ticket\": \"ENG-412\"\n },\n \"version\": 3,\n \"maxConcurrency\": 5,\n \"itemTimeout\": 60000,\n \"maxRetries\": 2,\n \"variables\": {\n \"locale\": \"English\",\n \"instance\": {\n \"store_count\": 12\n }\n },\n \"strictVariables\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-User-Id", "<x-user-id>")
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.sidenet.ai/v1/experiments")
.header("X-User-Id", "<x-user-id>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"agentId\": \"6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10\",\n \"datasetId\": \"ds_orders_v3\",\n \"scorers\": [\n \"completeness\",\n \"tone\"\n ],\n \"async\": false,\n \"name\": \"Refund tone — August\",\n \"description\": \"Checks the refund answers after the new brand-voice block\",\n \"metadata\": {\n \"ticket\": \"ENG-412\"\n },\n \"version\": 3,\n \"maxConcurrency\": 5,\n \"itemTimeout\": 60000,\n \"maxRetries\": 2,\n \"variables\": {\n \"locale\": \"English\",\n \"instance\": {\n \"store_count\": 12\n }\n },\n \"strictVariables\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sidenet.ai/v1/experiments")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-User-Id"] = '<x-user-id>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"agentId\": \"6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10\",\n \"datasetId\": \"ds_orders_v3\",\n \"scorers\": [\n \"completeness\",\n \"tone\"\n ],\n \"async\": false,\n \"name\": \"Refund tone — August\",\n \"description\": \"Checks the refund answers after the new brand-voice block\",\n \"metadata\": {\n \"ticket\": \"ENG-412\"\n },\n \"version\": 3,\n \"maxConcurrency\": 5,\n \"itemTimeout\": 60000,\n \"maxRetries\": 2,\n \"variables\": {\n \"locale\": \"English\",\n \"instance\": {\n \"store_count\": 12\n }\n },\n \"strictVariables\": true\n}"
response = http.request(request)
puts response.read_body{
"status": "started",
"experimentId": "exp_7f31a9c2",
"variables": {
"supplied": [
"locale"
],
"required": [
"locale",
"instance.store_count"
],
"missing": [
"instance.store_count"
],
"dropped": [],
"unused": []
}
}Run experiment
Runs a dataset experiment against an agent. Send agentId (agent id or agent version id), datasetId, and optional scorers (scorer types from the catalog, e.g. “tone”, “bias” — code scorers and LLM judges both supported). Set async: true for fire-and-forget (returns an experimentId to poll GET /api/datasets//experiments/); omit it to block and receive the full summary. Requires X-User-Id. Each dataset item runs in its own conversation thread by default, so items never share memory state and can run concurrently. To pin an item to a specific thread (e.g. several items forming one conversation), set requestContext.threadId on that dataset item — and run with maxConcurrency: 1 if multiple items share a thread. Send variables to fill the in the agent’s prompt blocks for every item; a dataset item’s own requestContext.variables are merged over them leaf by leaf, so a row overrides only what it varies. By default the run is REFUSED (400) when a placeholder with no || 'default' has no value — pass strictVariables: false to run anyway. The response always carries a variables report (supplied, required, missing, dropped, unused).
curl --request POST \
--url https://api.sidenet.ai/v1/experiments \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'X-User-Id: <x-user-id>' \
--data '
{
"agentId": "6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10",
"datasetId": "ds_orders_v3",
"scorers": [
"completeness",
"tone"
],
"async": false,
"name": "Refund tone — August",
"description": "Checks the refund answers after the new brand-voice block",
"metadata": {
"ticket": "ENG-412"
},
"version": 3,
"maxConcurrency": 5,
"itemTimeout": 60000,
"maxRetries": 2,
"variables": {
"locale": "English",
"instance": {
"store_count": 12
}
},
"strictVariables": true
}
'import requests
url = "https://api.sidenet.ai/v1/experiments"
payload = {
"agentId": "6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10",
"datasetId": "ds_orders_v3",
"scorers": ["completeness", "tone"],
"async": False,
"name": "Refund tone — August",
"description": "Checks the refund answers after the new brand-voice block",
"metadata": { "ticket": "ENG-412" },
"version": 3,
"maxConcurrency": 5,
"itemTimeout": 60000,
"maxRetries": 2,
"variables": {
"locale": "English",
"instance": { "store_count": 12 }
},
"strictVariables": True
}
headers = {
"X-User-Id": "<x-user-id>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-User-Id': '<x-user-id>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
agentId: '6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10',
datasetId: 'ds_orders_v3',
scorers: ['completeness', 'tone'],
async: false,
name: 'Refund tone — August',
description: 'Checks the refund answers after the new brand-voice block',
metadata: {ticket: 'ENG-412'},
version: 3,
maxConcurrency: 5,
itemTimeout: 60000,
maxRetries: 2,
variables: {locale: 'English', instance: {store_count: 12}},
strictVariables: true
})
};
fetch('https://api.sidenet.ai/v1/experiments', 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.sidenet.ai/v1/experiments",
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([
'agentId' => '6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10',
'datasetId' => 'ds_orders_v3',
'scorers' => [
'completeness',
'tone'
],
'async' => false,
'name' => 'Refund tone — August',
'description' => 'Checks the refund answers after the new brand-voice block',
'metadata' => [
'ticket' => 'ENG-412'
],
'version' => 3,
'maxConcurrency' => 5,
'itemTimeout' => 60000,
'maxRetries' => 2,
'variables' => [
'locale' => 'English',
'instance' => [
'store_count' => 12
]
],
'strictVariables' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"X-User-Id: <x-user-id>"
],
]);
$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.sidenet.ai/v1/experiments"
payload := strings.NewReader("{\n \"agentId\": \"6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10\",\n \"datasetId\": \"ds_orders_v3\",\n \"scorers\": [\n \"completeness\",\n \"tone\"\n ],\n \"async\": false,\n \"name\": \"Refund tone — August\",\n \"description\": \"Checks the refund answers after the new brand-voice block\",\n \"metadata\": {\n \"ticket\": \"ENG-412\"\n },\n \"version\": 3,\n \"maxConcurrency\": 5,\n \"itemTimeout\": 60000,\n \"maxRetries\": 2,\n \"variables\": {\n \"locale\": \"English\",\n \"instance\": {\n \"store_count\": 12\n }\n },\n \"strictVariables\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-User-Id", "<x-user-id>")
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.sidenet.ai/v1/experiments")
.header("X-User-Id", "<x-user-id>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"agentId\": \"6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10\",\n \"datasetId\": \"ds_orders_v3\",\n \"scorers\": [\n \"completeness\",\n \"tone\"\n ],\n \"async\": false,\n \"name\": \"Refund tone — August\",\n \"description\": \"Checks the refund answers after the new brand-voice block\",\n \"metadata\": {\n \"ticket\": \"ENG-412\"\n },\n \"version\": 3,\n \"maxConcurrency\": 5,\n \"itemTimeout\": 60000,\n \"maxRetries\": 2,\n \"variables\": {\n \"locale\": \"English\",\n \"instance\": {\n \"store_count\": 12\n }\n },\n \"strictVariables\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sidenet.ai/v1/experiments")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-User-Id"] = '<x-user-id>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"agentId\": \"6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10\",\n \"datasetId\": \"ds_orders_v3\",\n \"scorers\": [\n \"completeness\",\n \"tone\"\n ],\n \"async\": false,\n \"name\": \"Refund tone — August\",\n \"description\": \"Checks the refund answers after the new brand-voice block\",\n \"metadata\": {\n \"ticket\": \"ENG-412\"\n },\n \"version\": 3,\n \"maxConcurrency\": 5,\n \"itemTimeout\": 60000,\n \"maxRetries\": 2,\n \"variables\": {\n \"locale\": \"English\",\n \"instance\": {\n \"store_count\": 12\n }\n },\n \"strictVariables\": true\n}"
response = http.request(request)
puts response.read_body{
"status": "started",
"experimentId": "exp_7f31a9c2",
"variables": {
"supplied": [
"locale"
],
"required": [
"locale",
"instance.store_count"
],
"missing": [
"instance.store_count"
],
"dropped": [],
"unused": []
}
}Authorizations
Organization API key, generated in studio.sidenet.ai. Backend only — never in a browser.
Headers
User id. May also be sent in the request body or as a userId query param.
Body
"6f1c0f4e-2b7a-4a51-9a2f-0c9d1b3e5a10"
"ds_orders_v3"
Scorer types from the catalog (e.g. "tone", "bias"). Code scorers and LLM judges both supported.
["completeness", "tone"]
"Refund tone — August"
"Checks the refund answers after the new brand-voice block"
{ "ticket": "ENG-412" }
Dataset version to run against. Defaults to the newest.
3
5
Milliseconds one item may take before it is failed.
60000
Retries per dataset item before it is recorded as failed. Defaults to 2 so a transient provider fault does not fail an item; pass 0 to fail on the first error.
Values for the {{variable}} placeholders in the agent's prompt blocks, applied to every item. Scalars or arrays of them; a dot path up to 3 segments may be sent nested ({ "user": { "language": "FR" } }) or dotted ({ "user.language": "FR" }). Reserved: now.*. A dataset item's own requestContext.variables are merged over these leaf by leaf. Display-only — never used for authorization or identity.
{
"locale": "English",
"instance": { "store_count": 12 }
}
Refuse to start (400) when a placeholder with no default has no value for some item, or a supplied value is rejected by the limits. Pass false to run anyway and read the variables report on the response.
Response
The finished run, or — with async: true — the id of the run that was started
started on an async run; the scores are not ready yet.
completed, started Async runs only — poll the experiment by this id.
Sync runs only: the scorer results over the dataset. Its shape follows the scorers you asked for.
Show child attributes
Show child attributes