Back to System Backup Monitoring

Developer Handover Pack

Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.

4 pagesPDF · Technical
FS-DOC-04Technical

Developer Handover Pack

System Backup Monitoring

[YourCompany.com] · IT Department · Prepared by FullSpec · [Today's Date]

This document gives the FullSpec build team everything needed to implement the System Backup Monitoring automation end to end. It covers the current-state process map with bottleneck identification, full agent specifications with IO contracts, the end-to-end data flow trace, and the recommended build stack. The IT Administrator and process owner are responsible for confirming API access, maintaining the expected job list in Google Sheets, and configuring PagerDuty escalation policies before build begins. FullSpec handles all wiring, testing, and orchestration.

01Process snapshot

Step
Name
Description
1
Log Into Each Backup Platform
IT Administrator opens Veeam and each backup dashboard individually and navigates to the job log screen. No filtering or automation. Time cost: 20 minutes.
2
Scan Job Logs for Failures or Warnings
Administrator reads through the full overnight job list visually to identify failed, incomplete, or warning-state jobs. No automated filtering. This is the primary bottleneck. Time cost: 25 minutes.
3
Cross-Reference Against Expected Job List
Administrator checks a Google Sheets spreadsheet of expected jobs against the retrieved log, manually flagging any missing runs. Second major bottleneck. Time cost: 15 minutes.
4
Investigate the Cause of Each Failure
For each failed or missing job, the administrator reads the error message and determines whether it is a known or new issue. Time cost: 30 minutes.
5
Notify the Relevant Stakeholder
Administrator sends a Slack message or email to the application owner or manager whose system failed, summarising the failure. Time cost: 10 minutes.
6
Create an Incident Ticket
Administrator manually opens Jira and creates a ticket per failure, populating summary, priority, affected system, and error details. Time cost: 15 minutes.
7
Update the Backup Status Log
Administrator records each job result in the shared Google Sheet, noting date, job name, outcome, and ticket reference. Time cost: 15 minutes.
8
Escalate Critical Failures to Management
If a business-critical system is affected, the administrator sends a separate alert via Gmail to the IT manager or business owner. Time cost: 10 minutes.
9
Confirm Resolution and Close Ticket
Once the issue is resolved or a retry is confirmed successful, the administrator updates the Jira ticket and marks the Google Sheet row as resolved. Time cost: 10 minutes.
Time cost summary: Total manual time per cycle is 150 minutes (2.5 hours). At 5 cycles per week that is approximately 5.5 hours of manual effort per week, or roughly 4.5 hours of recoverable time once the human-required steps are excluded. Steps 1, 2, 3, 5, 6, 7, and 8 are fully replaced by the three agents. Steps 4 and 9 remain with the IT Administrator because failure investigation and resolution confirmation require human judgement.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Backup Status Collector

This agent connects to the Veeam REST API on a daily schedule and retrieves the full overnight job result set for the past 24 hours, including job name, status, start time, end time, and error message for each entry. It then reads the expected job list from a designated Google Sheets tab and cross-references the two datasets, producing a structured result list where every job carries a status of pass, fail, warning, or missing. This agent is the entry point for the entire automation cycle and must complete successfully before any downstream agent fires. Estimated build time: 10 hours. Complexity: Moderate.

Trigger
Daily schedule, configured time each morning (default: 06:00 local time). Fires once per day.
Tools
Veeam (REST API, read-only), Google Sheets (expected job list tab, read)
Replaces steps
Steps 1, 2, 3 (platform login, log scan, expected-job cross-reference)
Estimated build
10 hours, Moderate complexity
// Input
schedule.trigger: { fired_at: ISO8601 }
veeam.api.GET /api/v1/jobs/states: { timeframe: 'last_24h' }
google_sheets.read: { sheet_id: SHEET_ID, tab: 'expected_jobs', range: 'A:D' }
  -> columns: [job_name, system_type, severity_default, owner_slack_handle]

// Output
result_list: [
  {
    job_name: string,
    system_type: string,           // e.g. 'server', 'cloud', 'endpoint'
    status: 'pass' | 'fail' | 'warning' | 'missing',
    start_time: ISO8601 | null,
    end_time: ISO8601 | null,
    error_message: string | null,
    severity_default: 'critical' | 'warning' | 'informational',
    owner_slack_handle: string
  }
]
// Passed directly to Failure Classifier and Escalation Agent
  • Veeam API access requires Veeam Backup and Replication with REST API enabled; confirm the API user account has read-only scope on job states and session logs before build start. The API user must not have write permissions.
  • The Veeam API endpoint base URL and authentication method (Bearer token via OAuth2 or basic auth depending on Veeam version) must be confirmed by the IT team at discovery. Store credentials in the shared credential store, never hardcoded.
  • The Google Sheets expected job list must use a fixed tab name ('expected_jobs') and the column order: job_name (A), system_type (B), severity_default (C), owner_slack_handle (D). Any deviation will break the cross-reference logic.
  • If the Veeam API returns an empty result set (possible on a holiday or maintenance window), the agent must not pass an empty list downstream. It must emit a warning event and halt the cycle for that day, logging the empty-result condition to the audit sheet with a status of 'api_no_data'.
  • Missing jobs (present in the expected list but absent from the API result) must be assigned status 'missing' and severity_default from the expected job list row, not from the API response.
  • Dedupe: if the Veeam API returns duplicate entries for the same job name within the 24-hour window (e.g. a retry run), keep the latest end_time entry only.
  • Confirm Veeam API rate limits before build. The default REST API allows 10 concurrent sessions; this agent should use a single session and close it on completion.
Failure Classifier and Escalation Agent

This agent receives the structured result list from the Backup Status Collector and applies pre-configured severity classification rules to each anomalous entry. For any job with a status of fail, warning, or missing, it determines whether the severity should be escalated to critical based on the system_type and error_message fields. It then routes each anomaly to the correct notification channel: a Jira ticket is created per failure, a Slack alert is sent to the on-call owner, and a PagerDuty incident is triggered for any critical classification. Gmail is available as a fallback escalation channel if PagerDuty is unreachable. Jobs with a status of pass are passed through to the Audit Log Writer without action. Estimated build time: 14 hours. Complexity: Complex.

Trigger
Fires immediately on completion of the Backup Status Collector, receiving the result_list payload directly.
Tools
Jira (ticket creation), Slack (channel post and DM), PagerDuty (incident creation), Gmail (fallback escalation)
Replaces steps
Steps 4, 5, 6, 8 (failure investigation routing, stakeholder notification, ticket creation, critical escalation)
Estimated build
14 hours, Complex complexity
// Input
result_list: [ { job_name, system_type, status, start_time, end_time,
                  error_message, severity_default, owner_slack_handle } ]

// Classification logic (applied per entry where status != 'pass')
if system_type IN ['server', 'cloud'] AND status IN ['fail', 'missing']
  -> severity = 'critical'
if system_type == 'endpoint' OR status == 'warning'
  -> severity = 'warning'
// Override: if error_message matches known-critical error codes list
  -> severity = 'critical'  (takes precedence)

// Output (per anomalous job)
jira_ticket: {
  project_key: 'IT',
  issue_type: 'Incident',
  summary: '[BACKUP FAIL] {job_name} - {severity} - {end_time}',
  priority: severity == 'critical' ? 'High' : 'Medium',
  description: 'System: {system_type}\nStatus: {status}\nError: {error_message}\nTimestamp: {end_time}',
  labels: ['backup-monitoring', 'automated']
}
-> jira_ticket_id: string  // stored for audit log

slack_message: {
  channel: owner_slack_handle,
  text: ':warning: Backup failure: {job_name} ({severity}). Jira: {jira_ticket_url}'
}

// On critical severity only
pagerduty_incident: {
  routing_key: PAGERDUTY_INTEGRATION_KEY,
  event_action: 'trigger',
  payload: {
    summary: 'Critical backup failure: {job_name}',
    severity: 'critical',
    source: 'backup-monitoring-automation',
    custom_details: { job_name, system_type, error_message, jira_ticket_id }
  }
}

// Fallback: if PagerDuty API returns non-2xx, send Gmail alert
gmail_fallback: {
  to: IT_MANAGER_EMAIL,
  subject: '[CRITICAL BACKUP FAILURE - PAGERDUTY UNREACHABLE] {job_name}',
  body: 'PagerDuty escalation failed. Manual review required. Job: {job_name}, Error: {error_message}'
}
  • Jira project key must be confirmed by the IT team before build. Default is 'IT'. The automation platform's Jira service account must have permission to create Issues in that project. Use Jira API v3 (cloud) or v2 (server) depending on the client's Jira deployment type.
  • Slack integration requires an installed Slack app with the chat:write and chat:write.public OAuth scopes. The workspace bot token must be stored in the shared credential store. Confirm whether alerts go to a named channel (e.g. #backup-alerts) or as DMs to owner_slack_handle values.
  • PagerDuty integration uses the Events API v2. The routing_key (integration key) is obtained from the PagerDuty service configuration. On-call schedules and escalation policies must be configured in PagerDuty before this agent is wired, as the agent does not manage those policies.
  • The known-critical error codes list must be populated by the IT team at discovery. Store as a configurable list in the workflow, not hardcoded, so it can be updated without a rebuild.
  • If the result_list contains zero anomalies (all jobs passed), this agent must still fire and pass the full result_list to the Audit Log Writer with no tickets or alerts created. Do not short-circuit the chain.
  • Deduplication for Jira: before creating a ticket, check for an open Jira issue with the same job_name label created in the last 24 hours. If one exists, add a comment rather than creating a duplicate ticket.
  • Gmail fallback is only triggered if PagerDuty returns a non-2xx response after two retry attempts with a 10-second backoff. Log the PagerDuty failure to the error log table regardless.
Audit Log Writer

This agent fires after both the Backup Status Collector and the Failure Classifier and Escalation Agent have completed their run for the day. It appends one row to the central Google Sheets audit log tab for every job in the result_list, whether the job passed or failed. Each row records the job name, system type, outcome status, severity classification, Jira ticket reference (if one was created), PagerDuty incident ID (if triggered), and the run timestamp. This produces a complete, tamper-evident daily audit trail without any manual data entry. Estimated build time: 6 hours. Complexity: Simple.

Trigger
Fires after the Failure Classifier and Escalation Agent completes. Receives the full enriched result_list including ticket and incident references.
Tools
Google Sheets (audit log tab, append rows)
Replaces steps
Step 7 (manual backup status log update)
Estimated build
6 hours, Simple complexity
// Input
enriched_result_list: [
  {
    job_name: string,
    system_type: string,
    status: 'pass' | 'fail' | 'warning' | 'missing',
    severity: 'critical' | 'warning' | 'informational' | null,
    start_time: ISO8601 | null,
    end_time: ISO8601 | null,
    error_message: string | null,
    jira_ticket_id: string | null,
    jira_ticket_url: string | null,
    pagerduty_incident_id: string | null,
    run_date: ISO8601  // date of the automation cycle
  }
]

// Output
google_sheets.appendRow: {
  sheet_id: SHEET_ID,
  tab: 'audit_log',
  row: [
    run_date,          // column A
    job_name,          // column B
    system_type,       // column C
    status,            // column D
    severity,          // column E
    error_message,     // column F
    jira_ticket_id,    // column G
    jira_ticket_url,   // column H
    pagerduty_incident_id  // column I
  ]
}
// One appendRow call per job in enriched_result_list
// On completion: emit audit_write_confirmed: { rows_written: integer, run_date: ISO8601 }
  • The Google Sheets audit log tab must be named 'audit_log' exactly. The column order is fixed as: run_date (A), job_name (B), system_type (C), status (D), severity (E), error_message (F), jira_ticket_id (G), jira_ticket_url (H), pagerduty_incident_id (I). Do not alter this order post-launch without updating the agent mapping.
  • The Google Sheets service account must have Editor access to the spreadsheet. Use a dedicated service account, not a personal Google account, so access is not lost if a team member leaves.
  • Batch all appendRow calls into a single Google Sheets API batchUpdate request where possible to stay within the Sheets API quota (300 write requests per minute per project). For 120 jobs per month this is well within limits, but confirm the daily job count at discovery.
  • If the appendRow operation fails for any row, log the failure to the error log table and retry up to three times with exponential backoff. Do not silently drop rows from the audit trail.
  • This agent must always run even if the Failure Classifier returned zero anomalies. Pass-status rows must be written to the audit log for completeness.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow with field names at each agent handoff
// ─────────────────────────────────────────────────────────────────────
// TRIGGER
// ─────────────────────────────────────────────────────────────────────
schedule.daily_trigger: {
  fired_at: '2025-05-05T06:00:00Z'  // configurable; default 06:00 local
}

// ─────────────────────────────────────────────────────────────────────
// AGENT 1: Backup Status Collector
// ─────────────────────────────────────────────────────────────────────

// Step 1a: Pull job results from Veeam REST API
GET https://{veeam_host}/api/v1/jobs/states
  Authorization: Bearer {VEEAM_API_TOKEN}
  Query: { startTime: 'now-24h', endTime: 'now' }
  Response fields used: [
    job.name           -> job_name
    job.result         -> raw_status  // 'Success','Warning','Failed','Running'
    job.startTime      -> start_time
    job.endTime        -> end_time
    job.lastResult.message -> error_message
  ]

// Step 1b: Read expected job list from Google Sheets
GET google_sheets.values/{SHEET_ID}/expected_jobs!A:D
  Response columns: [job_name, system_type, severity_default, owner_slack_handle]

// Step 1c: Cross-reference and assign status
FOR each row IN expected_jobs:
  IF row.job_name NOT IN veeam_results -> status = 'missing'
  ELSE IF veeam_result.raw_status == 'Failed' -> status = 'fail'
  ELSE IF veeam_result.raw_status == 'Warning' -> status = 'warning'
  ELSE -> status = 'pass'

// Handoff 1 -> 2: result_list[]
result_list: [
  { job_name, system_type, status, start_time, end_time,
    error_message, severity_default, owner_slack_handle }
]

// ─────────────────────────────────────────────────────────────────────
// AGENT 2: Failure Classifier and Escalation Agent
// ─────────────────────────────────────────────────────────────────────

// Step 2a: Filter anomalies
anomalies = result_list.filter(r => r.status != 'pass')

// Step 2b: Classify severity per entry
FOR each entry IN anomalies:
  IF entry.system_type IN ['server','cloud'] AND entry.status IN ['fail','missing']
    -> entry.severity = 'critical'
  ELSE IF entry.error_message MATCHES known_critical_error_codes[]
    -> entry.severity = 'critical'  // override
  ELSE IF entry.status == 'warning' OR entry.system_type == 'endpoint'
    -> entry.severity = 'warning'
  ELSE
    -> entry.severity = 'informational'

// Step 2c: Create Jira ticket per anomaly
POST https://{jira_host}/rest/api/3/issue
  Authorization: Basic {JIRA_API_TOKEN}
  Body: {
    project.key: 'IT',
    issuetype.name: 'Incident',
    summary: '[BACKUP FAIL] {job_name} - {severity} - {end_time}',
    priority.name: severity=='critical' ? 'High' : 'Medium',
    description: { system_type, status, error_message, end_time },
    labels: ['backup-monitoring','automated']
  }
  Response: { id: jira_ticket_id, key: jira_ticket_key, self: jira_ticket_url }
  // Dedupe check: skip creation if open issue with same job_name label exists < 24h

// Step 2d: Send Slack alert per anomaly
POST https://slack.com/api/chat.postMessage
  Authorization: Bearer {SLACK_BOT_TOKEN}
  Body: {
    channel: entry.owner_slack_handle,
    text: ':warning: Backup failure: {job_name} ({severity}). Ticket: {jira_ticket_url}'
  }

// Step 2e: PagerDuty escalation (critical only)
IF entry.severity == 'critical':
  POST https://events.pagerduty.com/v2/enqueue
    Body: {
      routing_key: {PAGERDUTY_INTEGRATION_KEY},
      event_action: 'trigger',
      payload: {
        summary: 'Critical backup failure: {job_name}',
        severity: 'critical',
        source: 'backup-monitoring-automation',
        custom_details: { job_name, system_type, error_message, jira_ticket_id }
      },
      dedup_key: '{job_name}-{run_date}'  // prevents duplicate PD incidents
    }
  Response: { status: 'success', message: 'Event processed', incident_key: pd_incident_id }
  // On non-2xx after 2 retries -> Gmail fallback to IT_MANAGER_EMAIL

// Handoff 2 -> 3: enriched_result_list[]
enriched_result_list: [
  { job_name, system_type, status, severity, start_time, end_time,
    error_message, jira_ticket_id, jira_ticket_url,
    pagerduty_incident_id, run_date }
]

// ─────────────────────────────────────────────────────────────────────
// AGENT 3: Audit Log Writer
// ─────────────────────────────────────────────────────────────────────

// Step 3a: Append one row per job (pass AND fail) to audit_log tab
POST google_sheets.spreadsheets/{SHEET_ID}/values/audit_log!A:I:append
  Authorization: Bearer {GOOGLE_SERVICE_ACCOUNT_TOKEN}
  Body: {
    valueInputOption: 'USER_ENTERED',
    values: [
      [run_date, job_name, system_type, status, severity,
       error_message, jira_ticket_id, jira_ticket_url, pagerduty_incident_id]
    ]
  }
  // Batch all rows in one batchUpdate call

// Final output
audit_write_confirmed: {
  rows_written: integer,  // total jobs logged this cycle
  run_date: ISO8601
}
// Cycle complete. IT Administrator receives no output unless a ticket,
// Slack alert, or PagerDuty incident was triggered by Agent 2.
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation platform running one workflow per agent (three workflows total): Backup Status Collector, Failure Classifier and Escalation Agent, and Audit Log Writer. Workflows are chained by direct data passing rather than separate HTTP calls between them. All credentials are stored in a shared credential store scoped to this automation only. No platform-specific tool is mandated at this stage; the design is platform-agnostic.
Webhook and scheduling configuration
No inbound webhook is required. All workflows are triggered by a single internal daily schedule node set to 06:00 local time (configurable). The schedule fires Agent 1, which on completion passes its output directly to Agent 2, which on completion passes its enriched output to Agent 3. There is no external webhook surface to secure.
Templating approach
Jira ticket summaries and descriptions use string interpolation templates populated from the result_list fields at runtime. Slack message text follows the same pattern. Templates are stored as configurable variables within the workflow, not hardcoded strings, so the IT team can update message formats without a rebuild. The known-critical error codes list is stored as a workflow-level JSON array variable.
Error logging
All agent-level errors (Veeam API timeout, Jira creation failure, PagerDuty non-2xx, Sheets append failure) are written to a dedicated error log table (recommended: a Supabase table named 'backup_monitor_errors') with fields: error_timestamp, agent_name, error_type, error_detail, job_name (if applicable), retry_count, resolved (boolean). A Slack alert to a designated #automation-errors channel fires when any row is inserted into this table. This ensures the FullSpec team or the IT Administrator can detect automation failures independently of the process itself.
Testing approach
All three agents are built and tested against a staging environment first. The Veeam API is tested with a read-only staging credential against a non-production job set. Google Sheets tests use a duplicate spreadsheet with a cloned expected_jobs tab. Jira tickets are created in a sandbox project (key: 'TEST') and deleted post-test. Slack and PagerDuty use test channels and a PagerDuty development service key. Only after full end-to-end staging validation does the credential store switch to production values. Simulated failure scenarios include: API timeout, empty result set, all-pass result, single critical failure, multiple mixed-severity failures, and missing PagerDuty key.
Estimated total build time
Backup Status Collector: 10 hours. Failure Classifier and Escalation Agent: 14 hours. Audit Log Writer: 6 hours. End-to-end integration testing and staging validation: 2 hours (included in the 32-hour build effort total). Grand total: 32 hours across approximately 3 to 4 build weeks as scoped.
Pre-build blockers: three items must be confirmed before development begins. First, the Veeam API user account with read-only access to job states must be provisioned and credentials provided to the FullSpec team via the secure credential intake form. Second, the expected job list in Google Sheets must be current and in the correct column format (job_name, system_type, severity_default, owner_slack_handle) before the Backup Status Collector is tested. Third, PagerDuty escalation policies and on-call schedules must be fully configured in the PagerDuty account before the Failure Classifier is wired, as the agent cannot create or modify those policies. Contact support@gofullspec.com to submit credentials and confirm readiness.
Credential / Config Item
Agent
Storage Location
Status
VEEAM_API_TOKEN + veeam_host URL
Backup Status Collector
Shared credential store
To confirm
SHEET_ID (Google Sheets)
All agents
Shared credential store
To confirm
GOOGLE_SERVICE_ACCOUNT_TOKEN
Backup Status Collector, Audit Log Writer
Shared credential store
To confirm
JIRA_API_TOKEN + jira_host
Failure Classifier and Escalation Agent
Shared credential store
To confirm
SLACK_BOT_TOKEN
Failure Classifier and Escalation Agent
Shared credential store
To confirm
PAGERDUTY_INTEGRATION_KEY
Failure Classifier and Escalation Agent
Shared credential store
To confirm
IT_MANAGER_EMAIL (Gmail fallback)
Failure Classifier and Escalation Agent
Workflow variable
To confirm
known_critical_error_codes[]
Failure Classifier and Escalation Agent
Workflow variable (JSON array)
IT team to supply
Supabase error log table connection
All agents (error handler)
Shared credential store
FullSpec provisions
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for System Backup Monitoring.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Integration and API Spec
Technical · Developer
View
Test and QA Plan
Quality · Developer
View