curl "https://api.signalmd.dev/v1/agents/6f1d2c3b-4a5e-4f60-8b7a-9c0d1e2f3a4b/versions/compare?from=7&to=8" \
-H "Authorization: Bearer $SIGNAL_API_KEY"import requests
url = "https://api.signalmd.dev/v1/agents/{agentId}/versions/compare"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.signalmd.dev/v1/agents/{agentId}/versions/compare', 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}/versions/compare",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.signalmd.dev/v1/agents/{agentId}/versions/compare"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.signalmd.dev/v1/agents/{agentId}/versions/compare")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.signalmd.dev/v1/agents/{agentId}/versions/compare")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"object": "version_diff",
"from": {
"version": 123,
"configCaptured": true,
"configHash": "sha256:9f2c..."
},
"to": {
"version": 123,
"configCaptured": true,
"configHash": "sha256:9f2c..."
},
"changes": [
{
"kind": "node_added",
"location": {
"kind": "node",
"id": "<string>",
"label": "<string>"
},
"detail": "<string>",
"before": "<string>",
"after": "<string>"
}
],
"result": {
"conclusion": "insufficient_evidence",
"conclusionLabel": "Not enough calls",
"callsBefore": 123,
"callsAfter": 123,
"metrics": [
{
"metric": "got_it_done",
"label": "Got it done",
"unit": "share",
"before": {
"value": 123,
"n": 123
},
"after": {
"value": 123,
"n": 123
},
"delta": 123,
"better": "higher",
"verdict": "changed",
"direction": "better",
"minimum": "The difference is real.",
"notRead": 123
}
],
"issueMoves": [
{
"issueId": "<string>",
"title": "<string>",
"place": "<string>",
"before": {
"value": 123,
"n": 123
},
"after": {
"value": 123,
"n": 123
},
"callsBefore": 123,
"callsAfter": 123,
"delta": 123,
"verdict": "changed",
"direction": "better",
"atChangedPlace": true
}
],
"narrative": "<string>",
"rule": "Not enough calls on each version yet.",
"isRandomized": true
},
"comparison": {
"object": "comparison",
"id": "3f1c2d4e-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"fromVersion": 123,
"toVersion": 123,
"configCaptured": {
"from": true,
"to": true
},
"changes": [
{
"kind": "node_added",
"location": {
"kind": "node",
"id": "<string>",
"label": "<string>"
},
"detail": "<string>",
"before": "<string>",
"after": "<string>"
}
],
"result": {
"conclusion": "insufficient_evidence",
"conclusionLabel": "Not enough calls",
"callsBefore": 123,
"callsAfter": 123,
"metrics": [
{
"metric": "got_it_done",
"label": "Got it done",
"unit": "share",
"before": {
"value": 123,
"n": 123
},
"after": {
"value": 123,
"n": 123
},
"delta": 123,
"better": "higher",
"verdict": "changed",
"direction": "better",
"minimum": "The difference is real.",
"notRead": 123
}
],
"issueMoves": [
{
"issueId": "<string>",
"title": "<string>",
"place": "<string>",
"before": {
"value": 123,
"n": 123
},
"after": {
"value": 123,
"n": 123
},
"callsBefore": 123,
"callsAfter": 123,
"delta": 123,
"verdict": "changed",
"direction": "better",
"atChangedPlace": true
}
],
"narrative": "<string>",
"rule": "Not enough calls on each version yet.",
"isRandomized": true
},
"createdAt": "2026-10-02T14:03:07.120Z",
"updatedAt": "2026-10-02T14:03:07.120Z"
}
}{
"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"
}
}Compare two versions
What changed in the configuration between any two versions, not only neighbours, and what production did across them by the health board’s metrics and verdicts (result), with the stored comparison when one exists for that pair.
curl "https://api.signalmd.dev/v1/agents/6f1d2c3b-4a5e-4f60-8b7a-9c0d1e2f3a4b/versions/compare?from=7&to=8" \
-H "Authorization: Bearer $SIGNAL_API_KEY"import requests
url = "https://api.signalmd.dev/v1/agents/{agentId}/versions/compare"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.signalmd.dev/v1/agents/{agentId}/versions/compare', 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}/versions/compare",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.signalmd.dev/v1/agents/{agentId}/versions/compare"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.signalmd.dev/v1/agents/{agentId}/versions/compare")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.signalmd.dev/v1/agents/{agentId}/versions/compare")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"object": "version_diff",
"from": {
"version": 123,
"configCaptured": true,
"configHash": "sha256:9f2c..."
},
"to": {
"version": 123,
"configCaptured": true,
"configHash": "sha256:9f2c..."
},
"changes": [
{
"kind": "node_added",
"location": {
"kind": "node",
"id": "<string>",
"label": "<string>"
},
"detail": "<string>",
"before": "<string>",
"after": "<string>"
}
],
"result": {
"conclusion": "insufficient_evidence",
"conclusionLabel": "Not enough calls",
"callsBefore": 123,
"callsAfter": 123,
"metrics": [
{
"metric": "got_it_done",
"label": "Got it done",
"unit": "share",
"before": {
"value": 123,
"n": 123
},
"after": {
"value": 123,
"n": 123
},
"delta": 123,
"better": "higher",
"verdict": "changed",
"direction": "better",
"minimum": "The difference is real.",
"notRead": 123
}
],
"issueMoves": [
{
"issueId": "<string>",
"title": "<string>",
"place": "<string>",
"before": {
"value": 123,
"n": 123
},
"after": {
"value": 123,
"n": 123
},
"callsBefore": 123,
"callsAfter": 123,
"delta": 123,
"verdict": "changed",
"direction": "better",
"atChangedPlace": true
}
],
"narrative": "<string>",
"rule": "Not enough calls on each version yet.",
"isRandomized": true
},
"comparison": {
"object": "comparison",
"id": "3f1c2d4e-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"fromVersion": 123,
"toVersion": 123,
"configCaptured": {
"from": true,
"to": true
},
"changes": [
{
"kind": "node_added",
"location": {
"kind": "node",
"id": "<string>",
"label": "<string>"
},
"detail": "<string>",
"before": "<string>",
"after": "<string>"
}
],
"result": {
"conclusion": "insufficient_evidence",
"conclusionLabel": "Not enough calls",
"callsBefore": 123,
"callsAfter": 123,
"metrics": [
{
"metric": "got_it_done",
"label": "Got it done",
"unit": "share",
"before": {
"value": 123,
"n": 123
},
"after": {
"value": 123,
"n": 123
},
"delta": 123,
"better": "higher",
"verdict": "changed",
"direction": "better",
"minimum": "The difference is real.",
"notRead": 123
}
],
"issueMoves": [
{
"issueId": "<string>",
"title": "<string>",
"place": "<string>",
"before": {
"value": 123,
"n": 123
},
"after": {
"value": 123,
"n": 123
},
"callsBefore": 123,
"callsAfter": 123,
"delta": 123,
"verdict": "changed",
"direction": "better",
"atChangedPlace": true
}
],
"narrative": "<string>",
"rule": "Not enough calls on each version yet.",
"isRandomized": true
},
"createdAt": "2026-10-02T14:03:07.120Z",
"updatedAt": "2026-10-02T14:03:07.120Z"
}
}{
"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"
Query Parameters
The older version, usually.
x >= 08
The newer version, usually.
x >= 08
Response
The diff, the comparison and the stored comparison.
The configuration diff between two versions and what production did across them, with the stored comparison when SignalMD recorded one for that pair.
version_diff Show child attributes
Show child attributes
Show child attributes
Show child attributes
What changed in the configuration from from to to, computed now from the captured configurations. Null when either was not captured: it cannot be diffed, which is not the same as nothing changed.
Show child attributes
Show child attributes
What production did from from to to, read the same way as a stored comparison and the web app's version page, so its Got it done on each side equals that version's gotItDone in the version list. Any pair, recorded or not.
Show child attributes
Show child attributes
The stored comparison for exactly this pair, in this order, when SignalMD recorded one (its id is what get_comparison takes). Null otherwise; most pairs that are not consecutive versions have none. Its result reads the same calls as result.
Show child attributes
Show child attributes
Was this page helpful?