curl -X POST "https://api.signalmd.dev/v1/agents/6f1d2c3b-4a5e-4f60-8b7a-9c0d1e2f3a4b/watches/<watchId>/state" \
-H "Authorization: Bearer $SIGNAL_API_KEY"import requests
url = "https://api.signalmd.dev/v1/agents/{agentId}/watches/{watchId}/state"
payload = {}
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({})
};
fetch('https://api.signalmd.dev/v1/agents/{agentId}/watches/{watchId}/state', 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.signalmd.dev/v1/agents/{agentId}/watches/{watchId}/state",
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([
]),
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.signalmd.dev/v1/agents/{agentId}/watches/{watchId}/state"
payload := strings.NewReader("{}")
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.signalmd.dev/v1/agents/{agentId}/watches/{watchId}/state")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.signalmd.dev/v1/agents/{agentId}/watches/{watchId}/state")
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 = "{}"
response = http.request(request)
puts response.read_body{
"object": "watch",
"id": "3f1c2d4e-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"name": "<string>",
"lane": "hearing",
"state": "draft",
"revision": 123,
"origin": "<string>",
"passRate": 123,
"calls": 123,
"precision": 123,
"labelled": 123,
"source": {
"agentVersion": 123,
"step": "<string>",
"line": "<string>",
"verbatim": true
},
"firstSample": {
"inScope": 123,
"flagged": 123
},
"createdAt": "2026-10-02T14:03:07.120Z",
"updatedAt": "2026-10-02T14:03:07.120Z",
"scope": {
"text": "<string>",
"code": {
"kind": "tool_called",
"tool": "<string>",
"step": "<string>"
}
},
"expectation": {
"text": "<string>",
"polarity": "must",
"code": {}
},
"createdBy": "<string>",
"backtest": {
"watchRevision": 123,
"considered": 123,
"inScope": 123,
"flagged": 123,
"createdAt": "2026-10-02T14:03:07.120Z",
"updatedAt": "2026-10-02T14:03:07.120Z"
},
"samples": [
{
"callId": "3f1c2d4e-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"kind": "flagged",
"verdict": "<string>",
"quote": "<string>",
"turn": 123,
"label": "right"
}
],
"labels": {
"right": 123,
"wrong": 123,
"labelled": 123,
"precision": 123
},
"goLiveBlock": "<string>"
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}Set a watch's state
Moves the watch between draft, backtesting, review, live, paused and rejected. Moving to backtesting starts a backtest, which moves the watch to review when it finishes: this is how a suggested draft is kept; rejected dismisses it. Going live needs enough sample calls on the current revision labelled right, and only a person may do it (a key or the copilot gets 403). When it is refused, goLiveBlock says what is missing.
curl -X POST "https://api.signalmd.dev/v1/agents/6f1d2c3b-4a5e-4f60-8b7a-9c0d1e2f3a4b/watches/<watchId>/state" \
-H "Authorization: Bearer $SIGNAL_API_KEY"import requests
url = "https://api.signalmd.dev/v1/agents/{agentId}/watches/{watchId}/state"
payload = {}
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({})
};
fetch('https://api.signalmd.dev/v1/agents/{agentId}/watches/{watchId}/state', 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.signalmd.dev/v1/agents/{agentId}/watches/{watchId}/state",
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([
]),
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.signalmd.dev/v1/agents/{agentId}/watches/{watchId}/state"
payload := strings.NewReader("{}")
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.signalmd.dev/v1/agents/{agentId}/watches/{watchId}/state")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.signalmd.dev/v1/agents/{agentId}/watches/{watchId}/state")
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 = "{}"
response = http.request(request)
puts response.read_body{
"object": "watch",
"id": "3f1c2d4e-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"name": "<string>",
"lane": "hearing",
"state": "draft",
"revision": 123,
"origin": "<string>",
"passRate": 123,
"calls": 123,
"precision": 123,
"labelled": 123,
"source": {
"agentVersion": 123,
"step": "<string>",
"line": "<string>",
"verbatim": true
},
"firstSample": {
"inScope": 123,
"flagged": 123
},
"createdAt": "2026-10-02T14:03:07.120Z",
"updatedAt": "2026-10-02T14:03:07.120Z",
"scope": {
"text": "<string>",
"code": {
"kind": "tool_called",
"tool": "<string>",
"step": "<string>"
}
},
"expectation": {
"text": "<string>",
"polarity": "must",
"code": {}
},
"createdBy": "<string>",
"backtest": {
"watchRevision": 123,
"considered": 123,
"inScope": 123,
"flagged": 123,
"createdAt": "2026-10-02T14:03:07.120Z",
"updatedAt": "2026-10-02T14:03:07.120Z"
},
"samples": [
{
"callId": "3f1c2d4e-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"kind": "flagged",
"verdict": "<string>",
"quote": "<string>",
"turn": 123,
"label": "right"
}
],
"labels": {
"right": 123,
"wrong": 123,
"labelled": 123,
"precision": 123
},
"goLiveBlock": "<string>"
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}{
"error": {
"type": "not_found_error",
"code": "agent_not_found",
"message": "Agent not found.",
"param": null,
"requestId": "req_8c1f0e2b4a6d9e3f7a5c1b0d"
}
}Authorizations
An API key (sig_...), or a session token from the web app.
Path Parameters
The agent's id.
^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"3f1c2d4e-5a6b-4c7d-8e9f-0a1b2c3d4e5f"
The watch's id.
^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"3f1c2d4e-5a6b-4c7d-8e9f-0a1b2c3d4e5f"
Body
draft/backtesting while authoring; review once a backtest finished; live once a person turns it on; paused or rejected leave it off.
draft, backtesting, review, live, paused, rejected Response
The watch.
watch ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"3f1c2d4e-5a6b-4c7d-8e9f-0a1b2c3d4e5f"
The lane a defect lives in: hearing, understanding, flow, speaking or integrations.
hearing, understanding, flow, speaking, integrations draft/backtesting while authoring; review once a backtest finished; live once a person turns it on; paused or rejected leave it off.
draft, backtesting, review, live, paused, rejected Share of production calls in scope where the expectation held. Null until there are calls to measure.
Production calls behind the pass rate. 0 until there are any.
Of the sample calls a person labelled on the current revision, the share labelled right. Null until any are labelled.
For a generated watch (origin generated): the agent version and instruction line it was drafted from. Null otherwise.
Show child attributes
Show child attributes
On a suggested draft: how many past calls the first backtest found in scope and how many it flagged. Null until that backtest finishes. Precision is not known until the draft is kept and its sample calls are labelled.
Show child attributes
Show child attributes
"2026-10-02T14:03:07.120Z"
"2026-10-02T14:03:07.120Z"
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Why Turn on is refused, or null when the bar is met.
Was this page helpful?