Skip to content

Feedback API

The Feedback API records what was actually in a clip: whether it was an intrusion, whether that intrusion was a real incident, and anything else you want to attach. It is the recommended integration path for operator feedback, replacing the labels endpoint for integrators.

Report what you saw, not whether Gaard was right. You usually know the ground truth from your own alarm handling, while judging Gaard’s output would mean knowing what it concluded first. Send intrusion and Gaard derives the verdict for you (see Declaration and derived verdict).

Feedback is append-only. Every submission creates a new record; corrections are new submissions, never edits. Feedback you post is visible to your team in the platform’s Feedback view, including your custom metadata, with full fidelity.

POST /v1/feedback

The body combines a small strict envelope with anything else you want to send. Unknown fields are not an error: the entire request body is preserved verbatim and stays available for later processing.

{
"analyse_id": 999001,
"intrusion": "no",
"labels": ["person"],
"comment": "false positive - headlight reflection",
"severity": "high",
"real_incident": false,
"metadata": {
"expected_total": 5,
"plane": "som",
"lens_diopter": -1.3
},
"integrator_custom_block": { "anything": ["goes", "here"] }
}
FieldTypeRequiredDescription
analyse_idnumberone of the twoYour analyse reference for the classification.
idstringone of the twoThe Gaard classification task id.
intrusionstringone of the twoyes or no: what you say was actually in the clip.
verdictstringone of the twocorrect or incorrect. Optional: derived from intrusion when you omit it.
labelsstring[]noSuggested labels for the clip.
commentstringnoFree-text explanation.
severitystringnolow, medium, high, or critical.
real_incidentbooleannoMarks a confirmed real incident. Drives the platform’s real-incident registry. Cannot be true alongside intrusion: "no".
metadataobjectnoFlat key-value context, rendered in the platform UI.

At least one of analyse_id or id is required. When possible, Gaard resolves the other reference plus the site and camera identity from the classification task.

At least one of intrusion or verdict is required, so a comment on its own is not a submission. Sending both is allowed: an explicit verdict is stored exactly as you sent it and is never overwritten by the derivation.

intrusion is your statement of ground truth. verdict is a statement about Gaard: whether its risk level for the clip agreed with you. When you send intrusion and omit verdict, Gaard derives it:

You declaredGaard’s risk levelDerived verdict
yesintrusioncorrect
yesdangercorrect
yessafeincorrect
nointrusionincorrect
nodangerincorrect
nosafecorrect

The derived verdict is binary: either the classification agreed with you or it did not. A danger risk level counts as an alarm rather than a hedge, because that is how the platform treats it: anything that is not safe is dispatched, raised to operators, and reviewed. If the platform acted on a clip and you say it should not have, that is a miss and it counts as one.

Before 19 August 2026, a danger risk level derived inconclusive. Stored rows are never recomputed, so feedback submitted before that date keeps the verdict it was given, and inconclusive remains a value you can filter on in the platform. Nothing produces it now. You may still send it explicitly, but we discourage it.

The stored verdict comes back empty when the clip cannot be assessed:

  • you referenced it by analyse_id only and no classification task resolved
  • the task belongs to a different flow than your credentials
  • the task exists but its classification had not produced a result yet, which happens when you submit while classification is still running

An empty verdict means “not assessable”: Gaard never judged the clip, so there is nothing to agree or disagree with. It is not a third verdict value, and it is not the same as the legacy inconclusive. The platform’s Feedback view labels it “Not assessable”.

Your declaration is always stored, whatever the verdict turns out to be.

metadata values are stored as strings and shown as a key-value panel on the video details in the platform:

  • strings pass through unchanged
  • numbers and booleans are stringified (5, -1.3, true)
  • nested objects and arrays are stored as JSON strings
  • null values are dropped

Everything you send, including fields Gaard does not understand, is kept byte-for-byte in the stored record. You do not need to fit your internal JSON into our schema to start sending feedback.

StatusMeaning
200Stored. The response body is { "success": true, "data": { ...stored record } }.
401 / 403Missing or invalid credentials.
413Body larger than 1 MiB.
422Unknown intrusion/verdict/severity, neither analyse_id nor id present, neither intrusion nor verdict present, or real_incident: true alongside intrusion: "no".

A contradictory real_incident: true with intrusion: "no" is rejected rather than quietly corrected, so your analytics never diverge from what you sent. Sending real_incident: true without any intrusion stays valid.

{
"success": true,
"data": {
"Id": "6a61e46b194b60cccf924696",
"AnalyseId": 999001,
"Intrusion": "no",
"Verdict": "incorrect",
"Severity": "high",
"RealIncident": 0,
"Labels": ["person"],
"Metadata": { "expected_total": "5", "plane": "som", "lens_diopter": "-1.3" },
"Payload": "{ ...your request body, verbatim... }"
}
}

Two identical submissions create two records, both returning 200. This is intentional: feedback is a stream of judgments, and multiple reviewers (or repeated reviews) are all kept.

Use the same bearer authentication as the rest of the v1 API:

Authorization: Bearer <API_KEY>