Developer Handover Pack
Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.
Developer Handover Pack
Cybersecurity Incident Response
[YourCompany.com] · IT Department · Prepared by FullSpec · [Today's Date]
This document is the primary technical reference for the FullSpec team building the Cybersecurity Incident Response automation. It contains the full current-state process map with bottleneck flags, specifications for each of the three agents, the end-to-end data flow trace, and the recommended build stack with estimated hours. The IT Manager's role in the automated flow is preserved at the containment decision step; everything before and after that step is handled by the automation platform.
01Process snapshot
02Agent specifications
Classifies incoming Microsoft Defender alerts against a standardised severity rubric and automatically pulls supporting log data from Microsoft 365 to give the IT Manager a complete, pre-populated picture before they review the ticket. The agent removes the two costliest bottlenecks in the current process: unguided severity judgement and manual log retrieval across multiple consoles. It creates the Jira incident ticket with severity tag, alert summary, affected asset, and attached log excerpts, so the IT Manager opens a ticket that is already investigation-ready. Estimated build time: 16 hours. Complexity: Complex.
// Input
defender.alert {
alert_id: string,
alert_title: string,
alert_category: string, // e.g. 'Malware', 'Credential theft', 'Lateral movement'
affected_user_upn: string,
affected_device_id: string,
alert_severity_raw: string, // Defender's own severity label (pre-rubric)
detected_at: ISO8601 timestamp,
tenant_id: string
}
// Output
jira.ticket {
project_key: 'SEC',
issue_type: 'Incident',
summary: '[{severity_tag}] {alert_title} — {affected_user_upn}',
severity_tag: 'Critical' | 'High' | 'Medium' | 'Low',
description: {alert_summary} + {log_excerpt_markdown},
labels: [severity_tag],
custom_field_defender_alert_id: alert_id,
custom_field_affected_asset: affected_device_id,
custom_field_detected_at: detected_at,
attachments: [log_extract.json]
}
// Log pull from Microsoft 365
m365.audit_log_query {
user_upn: affected_user_upn,
device_id: affected_device_id,
time_window_hours: 24,
record_types: ['AzureActiveDirectory', 'Exchange', 'SharePoint', 'SecurityComplianceCenter']
}- Microsoft Defender API requires the SecurityEvents.Read.All and ThreatIndicators.Read.All application permissions in Azure AD. These must be granted by a Global Administrator before the build begins. Confirm this with the IT Manager before starting the Triage Agent build.
- Microsoft 365 audit log access requires the Office 365 Management Activity API with the ActivityFeed.Read application permission, plus unified audit logging enabled in the Microsoft Purview compliance portal. Confirm unified audit logging is active before testing.
- The severity rubric must be agreed and documented before the classification logic is coded. The rubric maps alert_category plus affected_asset_criticality to a four-point scale (Critical, High, Medium, Low). If the rubric is not signed off before build, the agent will default to Defender's native severity label as a fallback, which must be flagged in the ticket description.
- Jira project key SEC must exist and the automation platform service account must have the Create Issues and Attach Files permissions in that project. Confirm Jira tier: the Standard plan or above is required for API access.
- Deduplication: before creating a ticket, query Jira for open tickets with matching custom_field_defender_alert_id. If a match is found, update the existing ticket rather than creating a duplicate.
- Log excerpt size must be capped at 1 MB per attachment to avoid Jira attachment limits. Truncate and note in the description if the raw extract exceeds this.
Routes the incident notification to the correct Slack channel based on severity and, for Critical or High severity tickets, immediately escalates to the on-call responder via PagerDuty without waiting for a manual escalation decision. This agent eliminates the manual Slack drafting step and the separately triggered escalation step, reducing mean time to first notification from 15 to 45 minutes to under 2 minutes. The agent fires as soon as the Triage Agent creates the Jira ticket with a severity tag. Estimated build time: 10 hours. Complexity: Moderate.
// Input
jira.ticket {
ticket_id: string,
ticket_url: string,
severity_tag: 'Critical' | 'High' | 'Medium' | 'Low',
summary: string,
affected_user_upn: string,
detected_at: ISO8601 timestamp
}
// Output — all severity levels
slack.message {
channel: '#it-incidents', // or '#it-incidents-critical' for Critical
text: ':red_circle: [{severity_tag}] {summary}',
blocks: [
{ type: 'section', fields: [severity_tag, affected_user_upn, detected_at, ticket_url] }
]
}
// Output — Critical or High severity only
pagerduty.incident {
service_id: '<SEC_ONCALL_SERVICE_ID>',
title: '[{severity_tag}] {summary}',
body: 'Jira: {ticket_url} | Asset: {affected_user_upn} | Detected: {detected_at}',
urgency: 'high',
escalation_policy_id: '<ESCALATION_POLICY_ID>'
}- Slack channel routing table must be confirmed before build: Critical alerts route to '#it-incidents-critical', High and Medium to '#it-incidents', and Low to '#it-alerts-low' (or suppressed if agreed with the IT Manager). These channel names must be confirmed as existing in the Slack workspace.
- PagerDuty service_id and escalation_policy_id for the security on-call rotation must be provided before build. Confirm that the PagerDuty account is on the Professional plan or above to support API-triggered incidents.
- The Slack integration must use a dedicated bot token (xoxb-) with the chat:write and chat:write.public OAuth scopes. A bot user named 'FullSpec Incident Bot' should be created and added to each target channel.
- Fallback behaviour: if the PagerDuty API call fails for a Critical or High ticket, the agent must post a direct Slack message to the IT Manager's user ID as a fallback notification and log the PagerDuty failure to the error log table.
- A Jira webhook must be configured in the Jira project settings to POST to the automation platform's inbound webhook URL on issue_created events filtered to project SEC.
Compiles the full incident record in real time by pulling alert data, log excerpts, and all ticket updates from Jira into a structured Notion page as the incident progresses, then sends the post-incident summary to the Slack IT channel once the Jira ticket is resolved. This agent eliminates the highest-friction post-incident task: writing the report from memory. Because data is pulled from Jira at the point of resolution rather than reconstructed later, the Notion page is accurate, complete, and consistently formatted every time. Estimated build time: 12 hours. Complexity: Moderate.
// Input
jira.ticket_resolved {
ticket_id: string,
ticket_url: string,
severity_tag: string,
summary: string,
description: string,
affected_user_upn: string,
affected_device_id: string,
detected_at: ISO8601 timestamp,
resolved_at: ISO8601 timestamp,
resolution_notes: string,
comments: [{ author: string, body: string, created_at: ISO8601 }],
attachments: [{ filename: string, url: string }]
}
// Output — Notion page
notion.page {
parent_database_id: '<INCIDENT_LOG_DATABASE_ID>',
properties: {
Title: '[{severity_tag}] {summary}',
Severity: severity_tag,
Status: 'Resolved',
Detected: detected_at,
Resolved: resolved_at,
Affected_User: affected_user_upn,
Jira_Ticket: ticket_url
},
content_blocks: [
{ heading: 'Incident Summary', text: description },
{ heading: 'Timeline', text: comments_formatted },
{ heading: 'Log Excerpts', text: log_excerpt_from_attachment },
{ heading: 'Resolution', text: resolution_notes },
{ heading: 'Actions Taken', text: parsed_from_comments }
]
}
// Output — post-incident Slack summary
slack.message {
channel: '#it-incidents',
text: ':white_check_mark: RESOLVED [{severity_tag}] {summary}',
blocks: [
{ type: 'section', fields: [severity_tag, detected_at, resolved_at, affected_user_upn] },
{ type: 'actions', elements: [{ text: 'View Report', url: notion_page_url }] }
]
}- Notion integration requires an internal integration token with Insert content and Update content capabilities. The Notion incident log database must be shared with the integration before the agent is tested. Confirm the database ID with the IT Manager.
- Notion database must have the following properties pre-created with correct types: Title (title), Severity (select: Critical/High/Medium/Low), Status (select: Open/In Progress/Resolved), Detected (date), Resolved (date), Affected_User (rich text), Jira_Ticket (url). The agent will fail silently if property names do not match exactly.
- Jira comments are parsed in chronological order. The agent must handle empty comments arrays gracefully, inserting a 'No comments recorded' placeholder in the Timeline block rather than throwing an error.
- The agent must fetch the log_extract.json attachment from the Jira ticket via the Jira attachment API before constructing the Notion page. If the attachment is unavailable, the Log Excerpts block should note 'Log attachment not retrievable' and continue.
- A second Jira webhook must be configured on issue_updated events filtered to project SEC and status = 'Done'. Ensure this webhook does not overlap with the Notification Agent's issue_created webhook in a way that causes duplicate Slack messages on resolution.
03End-to-end data flow
// ============================================================
// TRIGGER
// ============================================================
// Source: Microsoft Defender Security Alerts API
// Event: New alert raised (alert_status = 'newAlert')
// Poll interval: 60 seconds OR webhook push if Defender webhook configured
defender.alert_payload {
alert_id,
alert_title,
alert_category,
affected_user_upn,
affected_device_id,
alert_severity_raw,
detected_at,
tenant_id
}
// ============================================================
// AGENT 1: TRIAGE AGENT
// ============================================================
// Step 1: Dedupe check — query Jira SEC project for open ticket
// with custom_field_defender_alert_id = alert_id
// IF match found -> update existing ticket, skip to AGENT 2
// IF no match -> continue
// Step 2: Severity classification via rubric lookup
// Inputs: alert_category, affected_device_id, affected_user_upn
// Rubric table: alert_category x asset_criticality -> severity_tag
// severity_tag: 'Critical' | 'High' | 'Medium' | 'Low'
// Fallback: if rubric returns no match -> use alert_severity_raw, flag in ticket
triage.classification {
severity_tag,
alert_summary, // constructed from alert_title + affected_user_upn + detected_at
rubric_match: boolean
}
// Step 3: Log retrieval from Microsoft 365
// API: Office 365 Management Activity API
// Query: affected_user_upn + affected_device_id, last 24 hours
// Record types: AzureActiveDirectory, Exchange, SharePoint, SecurityComplianceCenter
// Output: log_extract.json (capped at 1 MB)
m365.log_extract {
raw_events: [...],
extracted_at: ISO8601,
truncated: boolean
}
// Step 4: Jira ticket creation
// Project: SEC | Issue type: Incident
// Fields: summary, severity_tag, description (alert_summary + log_excerpt_markdown),
// custom_field_defender_alert_id, custom_field_affected_asset,
// custom_field_detected_at, labels=[severity_tag]
// Attachment: log_extract.json
jira.ticket_created {
ticket_id,
ticket_url,
severity_tag,
summary,
detected_at,
affected_user_upn
}
// ============================================================
// AGENT 1 -> AGENT 2 HANDOFF
// Trigger: Jira issue_created webhook fires on project SEC
// Payload passed: ticket_id, ticket_url, severity_tag, summary,
// affected_user_upn, detected_at
// ============================================================
// ============================================================
// AGENT 2: NOTIFICATION AGENT
// ============================================================
// Step 5: Slack channel routing
// severity_tag = 'Critical' -> #it-incidents-critical
// severity_tag = 'High' | 'Medium' -> #it-incidents
// severity_tag = 'Low' -> #it-alerts-low (or suppressed)
slack.message_sent {
channel,
severity_tag,
summary,
ticket_url,
timestamp: ISO8601
}
// Step 6: Escalation decision
// IF severity_tag IN ['Critical', 'High'] -> trigger PagerDuty
// ELSE -> no PagerDuty call
// Step 7: PagerDuty incident creation (Critical/High only)
// service_id: SEC_ONCALL_SERVICE_ID
// urgency: 'high'
// escalation_policy_id: ESCALATION_POLICY_ID
// Fallback on API failure: DM IT Manager Slack user ID + log error
pagerduty.incident_created {
pd_incident_id,
pd_incident_url,
triggered_at: ISO8601
}
// ============================================================
// HUMAN GATE — IT MANAGER REVIEW (NOT AUTOMATED)
// IT Manager reviews Jira ticket + Slack notification.
// Confirms or overrides containment action in Jira.
// Adds resolution notes and comments to ticket.
// Transitions ticket to 'Done' on resolution.
// Estimated time: 20 min (post-automation, investigation only).
// ============================================================
// ============================================================
// AGENT 2 -> AGENT 3 HANDOFF
// Trigger: Jira issue_updated webhook fires on project SEC,
// filtered to status_changed_to = 'Done'
// Payload passed: ticket_id, ticket_url, severity_tag, summary,
// description, affected_user_upn, affected_device_id,
// detected_at, resolved_at, resolution_notes,
// comments[], attachment_url (log_extract.json)
// ============================================================
// ============================================================
// AGENT 3: INCIDENT DOCUMENTATION AGENT
// ============================================================
// Step 8: Fetch Jira ticket full detail + comments + attachment
// attachment: GET /rest/api/3/attachment/content/{attachment_id}
// comments: GET /rest/api/3/issue/{ticket_id}/comment
// Step 9: Create Notion page
// Database: INCIDENT_LOG_DATABASE_ID
// Properties: Title, Severity, Status='Resolved', Detected,
// Resolved, Affected_User, Jira_Ticket
// Content blocks: Incident Summary, Timeline, Log Excerpts,
// Resolution, Actions Taken
notion.page_created {
notion_page_id,
notion_page_url,
created_at: ISO8601
}
// Step 10: Post-incident Slack summary
// Channel: #it-incidents
// Content: severity_tag, summary, detected_at, resolved_at,
// affected_user_upn, notion_page_url (linked button)
slack.post_incident_summary_sent {
channel: '#it-incidents',
notion_page_url,
timestamp: ISO8601
}
// ============================================================
// END OF AUTOMATED FLOW
// Complete audit trail: Jira ticket + Notion page + Slack thread
// ============================================================04Recommended build stack
More documents for this process
Every document generated for Cybersecurity Incident Response.