Home / API reference / Reports and summaries

Reports and summaries

Two kinds of document come out of SentrAI. An evidence report tells the story of one incident: what happened, who was told, what SentrAI did and whether the record behind it is intact. The client summary is one page for the business owner, covering the last day or week. The dashboard’s report buttons and the links in alerts fetch them from here.

Every endpoint here needs the API token (see Signing in) unless it says otherwise.

Evidence reports

GET /reports/{iid}

The evidence report for incident iid (such as RSK-2026-081), as JSON. It is built fresh on each call from the incident and the audit trail, so it always shows the current state.

curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8000/reports/RSK-2026-081

Returns (trimmed)

{
  "report_id": "ER-RSK-2026-081",
  "title": "Security Evidence Report",
  "generated_at": "2026-10-27T02:45:12+00:00",
  "incident_id": "RSK-2026-081",
  "incident": {
    "category": "brute_force",
    "severity": "high",
    "status": "resolved",
    "host": "web-01",
    "src_ip": "203.0.113.45",
    "user": "admin",
    "points": 45.0,
    "recommended_action": "Block 203.0.113.45, lock account 'admin' and rate-limit /login for 2 h"
  },
  "in_plain_words": "Password guessing on web-01 from 203.0.113.45, first seen 2026-10-27 02:45. ...",
  "outcome": "Closed at 2026-10-27 02:45.",
  "next_steps": ["If you do not recognise the address, keep it blocked.", "..."],
  "how_to_check": ["Every line of this report comes from the activity record on the server. ...", "..."],
  "responsible_entity": "On-duty responder",
  "handling": {
    "owner": "On-duty responder",
    "backup": "Team lead",
    "route": "High",
    "escalation_level": 0,
    "history": [ ... ]
  },
  "sla": {
    "policy_hours": 2.0,
    "breached": false,
    "summary": "Within SLA: 0 hrs 0 mins of 2 hrs used; acknowledged by erick at 2026-10-27T02:45:12+00:00"
  },
  "timeline_of_inaction": [ ... ]
}
Field Meaning
in_plain_words, outcome, next_steps the story for someone who is not technical
how_to_check how anyone can check the report against the audit trail
responsible_entity, handling who owned the incident, who backed them up, the routing rule, escalations and who closed it
sla the agreed response time and how much of it was used
timeline_of_inaction the incident’s timeline, with the time elapsed since it opened (demo_time, such as T+0h00m)

Errors: 404 incident RSK-2026-999 not found.

GET /reports/{iid}.md

The same report as Markdown (text/markdown), ready to paste into a ticket or email. This is the one version that also opens with a signed link (?sig=) instead of the token. The report_url in each alert is such a link.

Errors: 404 as above.

GET /reports/{iid}.pdf

The same report as a PDF, sent as a download named RSK-2026-081-evidence-report.pdf. Needs the token; signed links do not open it.

curl -H "Authorization: Bearer $TOKEN" -o report.pdf http://127.0.0.1:8000/reports/RSK-2026-081.pdf

Errors: 404 as above.

The client summary

GET /summary/{period}

The one-page summary for period, daily (the last 24 hours) or weekly (the last 7 days), as JSON.

Query

Name Type Default Meaning
until integer now end the period at this Unix time instead

Returns (trimmed)

{
  "summary_id": "SUM-D-20261027-0245",
  "title": "Daily security summary",
  "period": "daily",
  "from": "2026-10-26T02:45:12+00:00",
  "until": "2026-10-27T02:45:12+00:00",
  "until_ts": 1793069112,
  "period_text": "Tue 27 Oct 2026",
  "client": "Harbour Clinic",
  "headline": "4 threats today. 1 still needs your attention.",
  "numbers": {
    "threats": 4,
    "stopped_automatically": 2,
    "handled_by_staff": 3,
    "closed": 3,
    "need_attention": 1,
    "late_response": 0,
    "alerts_sent": 10,
    "alerts_delivered": 0,
    "alerts_acknowledged": 6
  },
  "risk_now": {"index": 28, "band": "green"},
  "attention": [
    {
      "incident": "RSK-2026-084",
      "what": "Staff report",
      "machine": "web-01",
      "severity": "medium",
      "open_for": "1 min",
      "seen_by_staff": false,
      "late": false,
      "next_step": "SentrAI is not sure this is an attack, so it did nothing on its own: please take a look."
    }
  ],
  "machines": [{"machine": "web-01", "threats": 2, "stopped": 2, "open": 0}],
  "kinds": [{"what": "Password guessing", "count": 1}],
  "actions": {
    "done": {"blocked an address": 2, "slowed down sign-in attempts": 1},
    "total": 4,
    "undone": 2,
    "expired": 0,
    "permanent": 2
  },
  "proof": {
    "chain_valid": true,
    "first_invalid_seq": null,
    "records": 68,
    "fingerprint": "SENTRAI-FP v1 seq=68 head=5c15f770... at=2026-10-27T02:45:12Z sig=d362579f...",
    "short": "#68 5c15f7703654efe3",
    "how_to_check": "Keep this summary. Your technical staff can check the line below against the record ..."
  },
  "responsible": "On-duty responder"
}

attention lists the incidents still open, proof.fingerprint is an audit fingerprint the reader can check later with GET /audit/verify. until_ts is what goes in ?until= to fetch the same period again.

Errors: 404 period must be one of daily, weekly.

GET /summary/{period}.md

The same summary as Markdown. Opens with the token or a signed link (?until=...&sig=...). Query and errors as above.

GET /summary/{period}.pdf

The same summary as a one-page PDF download (SUM-D-20261027-0245.pdf). Opens with the token or a signed link. Query and errors as above.

When the summary is sent

GET /summary/schedule

When the summary goes out and to whom.

Returns

{
  "every": "daily",
  "at": "08:00",
  "day": "mon",
  "to": "Client",
  "changed_by": null,
  "changed_at": null,
  "last_sent_at": "2026-10-27T02:45:11+00:00",
  "next_at": "2026-10-27T08:00:00+00:00"
}

every is daily, weekly or off; at is the local time of day; day is the weekday for a weekly summary; to is the name the summary is addressed to.

POST /summary/schedule

Changes the schedule. Send only what changes. Saved, and written to the audit trail.

Body

{"operator": "erick", "every": "weekly", "at": "08:30", "day": "mon", "to": "Harbour Clinic"}
Field Type Meaning
operator string, required who made the change
every string daily, weekly or off
at string time of day, HH:MM
day string weekday for weekly, such as mon or friday
to string who the summary is addressed to

Returns the schedule, as GET /summary/schedule.

Errors: 400 with every must be one of ..., day must be a weekday such as mon or friday or at must be a time of day such as 08:00.

POST /summary/send

Sends the summary for the scheduled period now, outside the schedule: it is queued as an alert of kind summary with a signed link to the PDF.

Body: {"operator": "erick"} (justification is accepted and ignored).

Returns

{
  "summary": { "summary_id": "SUM-W-20261027-0245", "period": "weekly", "...": "as GET /summary/{period}" },
  "notification": { "id": "ntf-0011", "kind": "summary", "...": "see Alerts and routing" },
  "link": "http://127.0.0.1:8000/summary/weekly.pdf?until=1793069112&sig=a432690e977d955040c8157901532a2c"
}

The alert record is described on Alerts and routing.