curl "https://api.signalmd.dev/v1/agents/6f1d2c3b-4a5e-4f60-8b7a-9c0d1e2f3a4b/issues/a2b3c4d5-e6f7-4a8b-9c0d-1e2f3a4b5c6d" \
-H "Authorization: Bearer $SIGNAL_API_KEY"import requests
url = "https://api.signalmd.dev/v1/agents/{agentId}/issues/{issueId}"
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}/issues/{issueId}', 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}/issues/{issueId}",
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}/issues/{issueId}"
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}/issues/{issueId}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.signalmd.dev/v1/agents/{agentId}/issues/{issueId}")
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": "issue",
"id": "a2b3c4d5-e6f7-4a8b-9c0d-1e2f3a4b5c6d",
"key": "tool_timeout:check_availability",
"code": "tool_timeout",
"title": "The agent did not reply in Book appointment",
"klass": "technical",
"lane": null,
"status": "discovered",
"source": "detector",
"countOnly": false,
"location": {
"kind": "tool",
"id": "check_availability",
"label": "check_availability"
},
"cause": {
"code": "tool_latency",
"label": "The availability lookup takes longer than the caller waits",
"detail": "check_availability answered after 9.4 s at the median on these calls; callers spoke again or hung up before it returned."
},
"causeEstablished": true,
"causeSummary": "The availability lookup takes longer than the caller waits",
"section": "fix",
"sectionNote": null,
"target": {
"kind": "tool",
"id": "check_availability",
"label": "check_availability"
},
"symptoms": [
{
"code": "dead_air",
"label": "Silence while a tool runs",
"count": 15
}
],
"intentSlug": "book_appointment",
"likelyCause": null,
"recommendation": "Cache the next seven days of openings, or have the agent say it is checking before the lookup starts.",
"volume": 15,
"rate": 0.03,
"failedOutcomeShare": 0.8,
"impact": "high",
"confidence": 0.86,
"priorityScore": 41.2,
"downstreamOf": null,
"consequences": [],
"firstSeenVersion": 6,
"lastSeenVersion": 8,
"fellOnLive": false,
"diagnosis": null,
"createdAt": "2026-09-12T10:01:44.000Z",
"updatedAt": "2026-10-02T08:30:12.000Z",
"evidenceCalls": 15,
"byVersion": [
{
"version": 7,
"calls": 9,
"total": 180,
"rate": 0.05
},
{
"version": 8,
"calls": 6,
"total": 178,
"rate": 0.0337
}
],
"graceDays": 7,
"fix": {
"state": "no_change",
"version": 8,
"liveDays": 10,
"graceDays": 7,
"text": "No change on v8 beyond noise: 6 of 178 on v8, against 9 of 180 before, over 10 days."
}
}{
"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"
}
}Get an issue
One issue: where, why, what to change, how sure, how many evidence calls, how its calls split by version, the agent’s grace period (graceDays) and whether the live version fixed it (fix), read with that grace period.
curl "https://api.signalmd.dev/v1/agents/6f1d2c3b-4a5e-4f60-8b7a-9c0d1e2f3a4b/issues/a2b3c4d5-e6f7-4a8b-9c0d-1e2f3a4b5c6d" \
-H "Authorization: Bearer $SIGNAL_API_KEY"import requests
url = "https://api.signalmd.dev/v1/agents/{agentId}/issues/{issueId}"
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}/issues/{issueId}', 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}/issues/{issueId}",
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}/issues/{issueId}"
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}/issues/{issueId}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.signalmd.dev/v1/agents/{agentId}/issues/{issueId}")
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": "issue",
"id": "a2b3c4d5-e6f7-4a8b-9c0d-1e2f3a4b5c6d",
"key": "tool_timeout:check_availability",
"code": "tool_timeout",
"title": "The agent did not reply in Book appointment",
"klass": "technical",
"lane": null,
"status": "discovered",
"source": "detector",
"countOnly": false,
"location": {
"kind": "tool",
"id": "check_availability",
"label": "check_availability"
},
"cause": {
"code": "tool_latency",
"label": "The availability lookup takes longer than the caller waits",
"detail": "check_availability answered after 9.4 s at the median on these calls; callers spoke again or hung up before it returned."
},
"causeEstablished": true,
"causeSummary": "The availability lookup takes longer than the caller waits",
"section": "fix",
"sectionNote": null,
"target": {
"kind": "tool",
"id": "check_availability",
"label": "check_availability"
},
"symptoms": [
{
"code": "dead_air",
"label": "Silence while a tool runs",
"count": 15
}
],
"intentSlug": "book_appointment",
"likelyCause": null,
"recommendation": "Cache the next seven days of openings, or have the agent say it is checking before the lookup starts.",
"volume": 15,
"rate": 0.03,
"failedOutcomeShare": 0.8,
"impact": "high",
"confidence": 0.86,
"priorityScore": 41.2,
"downstreamOf": null,
"consequences": [],
"firstSeenVersion": 6,
"lastSeenVersion": 8,
"fellOnLive": false,
"diagnosis": null,
"createdAt": "2026-09-12T10:01:44.000Z",
"updatedAt": "2026-10-02T08:30:12.000Z",
"evidenceCalls": 15,
"byVersion": [
{
"version": 7,
"calls": 9,
"total": 180,
"rate": 0.05
},
{
"version": 8,
"calls": 6,
"total": 178,
"rate": 0.0337
}
],
"graceDays": 7,
"fix": {
"state": "no_change",
"version": 8,
"liveDays": 10,
"graceDays": 7,
"text": "No change on v8 beyond noise: 6 of 178 on v8, against 9 of 180 before, over 10 days."
}
}{
"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 issue's id.
Response
The issue.
A problem worth fixing, named by a place, a cause (or "Cause not established" when nothing settled one) and a change.
issue ^[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"
Stable across re-analysis: the same problem at the same place keeps its key, so an issue can be followed from discovered to resolved.
What kind of problem it is: a signal code, or outcome.<lane> for an outcome issue.
Who a fix goes to. "technical": a tool, latency or platform defect, for an engineer. "flow": conversation design (routing, loops, confirmation handling). "content": prompts and knowledge (what the agent knows and says). "behaviour": something callers do that is worth knowing but is not a defect, so it is never counted among the problems.
technical, flow, content, behaviour The lane a fix belongs in, the one the board groups it under. Null where no lane fits, such as a call ending with no cause behind it.
hearing, understanding, flow, speaking, integrations, null Where the issue stands. "discovered": found by the analysis. "investigating": set by a person in the web app. "fix_deployed": a fix is live, marked by a person or read from the live version's configuration change at the issue's step or tool. "measuring": a fix is live and the live version has too few calls, or has not run for the grace period, to judge it. "resolved": not seen on any call for a grace period (7 days unless set per agent), marked resolved by a person, or rejected as not a real problem; a later sighting alone does not reopen it, and too few calls or no baseline keep it resolved ("Not enough calls to tell yet"). "regressed": it was resolved and its rate since then rose clearly over the rate before it. When the rate is back at about the old one, SignalMD reopens the issue (not as regressed) with "Fix did not work". An issue with no rate from before never regresses.
discovered, investigating, fix_deployed, measuring, resolved, regressed Which kind of finding this is, "detector" or "outcome". Some fields are set for only one kind, as their descriptions say. An agent's board shows one kind.
detector, outcome True for a count of calls that ended badly at one step with no cause established. Not a finding: the board lists these apart, under "Cause not established", and never ranks them among the issues.
Where it happens.
Show child attributes
Show child attributes
Why it happens, taken from what the trace shows. A hypothesis until a change proves it. Null when no cause was established; read it with causeEstablished.
Show child attributes
Show child attributes
Whether SignalMD established a cause. When false, say "cause not established": likelyCause reads that too, and no cause should be supplied in its place. The board prints it as a result, not a gap to fill.
One line on the cause, safe to repeat: the cause's label when it was established, otherwise "Cause not established."
"Cause not established."
Which section of the board the issue is in. "fix": a confirmed defect with an established cause, in the agent or outside it (then sectionNote says the fix belongs to whoever runs the failing service or platform, and that a change in the agent can at most soften it for callers). "improve": a change the customer controls (a prompt, flow, routing or tool setting) that moves a board metric, with the change and the metric in sectionNote. "not_established": a confirmed pattern whose cause is not established; the board lists it under "Cause not established", causeEstablished is false, sectionNote starts "Cause not established", and recommendation is what to check, never a change. Present it as a pattern whose cause is not established, never as a defect to fix, and never count it among the Fix items. Listings hold these three. Fetched by id, anything else reads "hidden" and should not be presented as a finding: an issue too small to be a confirmed pattern, a consequence folded under the issue that caused it (downstreamOf set), or what callers did that the agent could not have handled (klass "behaviour").
fix, improve, not_established, hidden "fix"
One sentence when the section needs it: a cause outside the agent and who has its fix, the change a lever is and the metric it moves, why the cause is not established, or why the issue is hidden. Null otherwise.
null
The thing someone would change to make it stop. Issues are grouped on this: one change, one issue.
Show child attributes
Show child attributes
How it was noticed, with how many calls showed each; one cause can surface more than one way. Outcome issues list their bad outcomes here.
Show child attributes
Show child attributes
What the callers wanted, when the issue is specific to one intent.
The issue page's cause line: a sentence or two, and a hypothesis, never a finding. Starts with "Cause not established" when causeEstablished is false, and then keeps only what was observed.
The suggested fix. Null on a count row, where there is no cause to fix.
Calls affected. For an outcome issue, only calls where the cause was confirmed.
Share of all analysed calls affected, 0 to 1.
Share of the affected calls that ended badly, 0 to 1. For an outcome issue, the share whose outcome outlasts the call (wrong details saved, nothing booked).
low, medium, high How sure SignalMD is that the issue is real and its cause right, 0 to 1. Not a measured probability: use it to compare issues, and read causeEstablished for whether there is a cause.
The board's rank, by impact. Higher first.
The key of the issue this one follows from. Set means it is a consequence, not a separate thing to fix. Listed only with includeDownstream=true.
Issues that follow from this one, most affected first. Fixing this one is expected to clear them.
Show child attributes
Show child attributes
The oldest version the issue's calls ran on. Null for outcome issues, which do not record it; see byVersion on the issue.
The newest version the issue's calls ran on. Null for outcome issues.
The live version fixed this issue: none of its calls shows it, judged only once that version has taken calls for the agent's grace period. Lists put such issues after every issue still happening, keeping the priority order within each group, as the board does. An issue that fell on the live version but is still seen on it is false here and keeps its place.
The diagnosis of an outcome issue. Null on detector issues.
Show child attributes
Show child attributes
"2026-10-02T14:03:07.120Z"
"2026-10-02T14:03:07.120Z"
Calls linked as evidence, listed by GET .../issues/{issueId}/calls. byVersion counts only the linked calls where the caller spoke, so its sum can be below this.
How the issue's calls split by version, newest first.
Show child attributes
Show child attributes
The agent's grace period in days (1 to 90, 7 by default): an open issue counts as resolved once the agent has taken calls for this long without it, and the live version is judged on an issue (fix) only once it has taken calls for this long.
7
Whether the live version fixed this issue, against every earlier version, read with the grace period. Null when there is no live version or no calls before it.
Show child attributes
Show child attributes
Set when the issue was reopened because the rate after a resolution did not meaningfully drop ("Fix did not work"), with the baseline and after counts. Null or absent otherwise. The page prints the reason with those rates once resolved_at is cleared.
Show child attributes
Show child attributes
Was this page helpful?