Back to Workplace Incident Reporting

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

Workplace Incident Reporting

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

This document is the complete technical reference for the Workplace Incident Reporting automation build. It covers the current-state process map, all three agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. The FullSpec team uses this pack to configure, wire, and test every component. Nothing in this document requires action from [YourCompany.com] beyond confirming the pre-build requirements called out in each agent section.

01Process snapshot

Step
Name
Description
1
Employee Submits Incident Details
The employee or a witness fills out a paper form or sends an email describing the incident, including what happened, when, where, and who was involved. Time cost: 15 min (employee-owned; not replaced by automation).
2
HR Receives and Logs the Report
HR manually re-enters incident details into the Google Sheets register and assigns a reference number by hand. Time cost: 20 min.
3
Classify Incident Severity
HR reads the description and assigns a severity level (minor, moderate, or serious) based on personal judgement with no scoring guide. Time cost: 10 min.
4
Notify Relevant Manager
HR emails the relevant line manager and, for serious incidents, the operations director, with incident details attached. Time cost: 15 min.
5
Create Investigation Task
HR manually writes an investigation task in Notion or sends a follow-up email asking the manager to investigate within a set number of days. BOTTLENECK. Time cost: 15 min.
6
Chase Manager for Investigation Update
When the investigation deadline passes, HR sends reminder emails manually if they remember to check the spreadsheet. BOTTLENECK. Time cost: 20 min.
7
Receive Investigation Findings
The manager emails findings and proposed corrective actions in free text, which HR copies into the incident register. Time cost: 15 min.
8
Assign and Track Corrective Actions
HR creates separate tasks for each corrective action and monitors them through follow-up emails or spreadsheet notes. BOTTLENECK. Time cost: 20 min.
9
Confirm Corrective Action Completion
HR checks whether corrective actions were completed by asking the responsible person again, often weeks after the deadline. Time cost: 10 min.
10
Update BambooHR Employee Record
For injury incidents, HR manually updates the employee record in BambooHR to note the incident date and outcome. Time cost: 10 min.
11
Close and Archive Incident Record
Once all corrective actions are confirmed, HR marks the incident closed on the spreadsheet and files supporting documents. Time cost: 10 min.
Time cost summary: Total manual time per incident cycle is 160 minutes (2 hrs 40 min), across steps 2 through 11. At 12 incidents per month, this equates to approximately 5 hours of HR coordinator time per week and 250 hours per year. The automation replaces steps 2, 3, 4, 5, 6, 7, 8, 9, and 10 entirely. Steps 1 (employee submission via Google Form) and 11 (HR formally closes the record in Notion) remain human-owned.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Incident Intake Agent

This agent fires the moment a new Google Form response lands. It parses every field from the form payload, applies the severity classification logic (minor, moderate, or serious) against agreed rule criteria covering injury type, number of people affected, and regulatory threshold flags, assigns a padded sequential reference number (e.g. INC-0042), and writes a fully structured row to the Google Sheets incident register. It is the entry point for the entire workflow and all downstream agents depend on the data it writes. Estimated build time: 6 hours. Complexity: Moderate.

Trigger
New Google Form response submitted (Forms trigger via the automation platform's native Google Forms connector or Apps Script webhook)
Tools
Google Forms, Google Sheets
Replaces steps
Steps 2 and 3: HR logging the report and manual severity classification
Estimated build
6 hours, Moderate
// Input
form_response: {
  submitted_at: ISO8601 timestamp,
  reporter_name: string,
  reporter_email: string,
  incident_date: date (YYYY-MM-DD),
  incident_time: string,
  incident_location: string,
  incident_type: enum [near-miss, injury, property-damage, hazard-report],
  injury_involved: boolean,
  injured_employee_id: string | null,
  description: string (free text),
  witnesses: string | null
}

// Output
sheets_row: {
  reference_number: string (e.g. INC-0042),
  submitted_at: ISO8601,
  reporter_name: string,
  reporter_email: string,
  incident_date: date,
  incident_type: string,
  injury_involved: boolean,
  injured_employee_id: string | null,
  severity: enum [minor, moderate, serious],
  description: string,
  status: 'open',
  notification_sent: false,
  investigation_due_date: date (incident_date + 5 business days for minor/moderate; + 2 for serious),
  corrective_actions_closed: false,
  bamboohr_updated: false
}
  • Severity classification rules must be documented and agreed with the HR manager before build begins. The rule set should map injury type and regulatory flag combinations to minor, moderate, or serious. A generic fallback is not acceptable for a compliance-sensitive process.
  • Google Sheets register must use the exact column order specified in the IO contract above. The sheet ID and tab name must be provided as build credentials before work starts.
  • Reference number format is INC-XXXX with zero-padding to four digits. The agent reads the last row to determine the next integer; a dedicated counter cell in a config tab is the preferred dedupe approach to avoid race conditions if two submissions arrive within the same second.
  • If the form response is missing required fields (e.g. incident_date is blank), the agent must write the row with a 'data-incomplete' status flag and send an alert email to the HR coordinator via Gmail rather than silently failing.
  • Google Workspace Business Starter or above is required for the Forms API trigger to function reliably at this volume. Confirm the account tier before build.
  • injury_involved boolean must be mapped explicitly from the form field label, not inferred from the description, to ensure BambooHR update logic downstream fires correctly.
Notification and Escalation Agent

This agent watches the Google Sheets incident register for new confirmed rows written by the Intake Agent. On each new row it reads the department field and severity to determine the correct Slack recipient: the line manager for that department receives a channel notification for all incidents; the operations director receives an additional direct message for any incident classified as serious. The agent also writes a notification log entry (recipient, channel or DM ID, timestamp, incident reference) back to a dedicated Notifications tab in the same Google Sheet. Estimated build time: 5 hours. Complexity: Moderate.

Trigger
New row confirmed in Google Sheets incident register with status 'open' and notification_sent = false
Tools
Slack, Google Sheets
Replaces steps
Step 4: HR manually emailing the relevant manager
Estimated build
5 hours, Moderate
// Input
sheets_row: {
  reference_number: string,
  severity: enum [minor, moderate, serious],
  incident_type: string,
  incident_date: date,
  incident_location: string,
  description: string (truncated to 300 chars for Slack preview),
  reporter_name: string,
  department: string  // used for manager routing lookup
}

// Output
slack_message_manager: {
  channel: '#incidents-[department]' or manager Slack user ID,
  text: 'New incident [reference_number] logged: [severity] | [incident_type] | [incident_date]. Please review and investigate within [investigation_due_date].',
  blocks: [incident summary card with reference, severity badge, due date, Notion link placeholder]
}
slack_message_director: {  // serious incidents only
  channel: '@ops-director-user-id',
  text: 'SERIOUS incident [reference_number] requires your immediate awareness. HR has been notified.'
}
sheets_notification_log_row: {
  reference_number: string,
  notified_at: ISO8601,
  manager_slack_id: string,
  director_notified: boolean,
  notification_sent: true
}
  • A department-to-Slack-channel mapping table must be provided before build. This should list every active department and its corresponding Slack channel ID or manager Slack user ID. Without this the routing logic cannot be built.
  • The operations director's Slack user ID must be confirmed and stored as a build credential. Do not hardcode a name; use the Slack user ID to avoid breakage if the director changes.
  • Slack Pro plan or above is required for reliable DM delivery and the Slack API scopes needed (chat:write, users:read, channels:read). Confirm the plan tier.
  • After sending notifications, the agent must update notification_sent to true in the Sheets row immediately to prevent duplicate notifications if the trigger re-fires.
  • If a Slack message fails to deliver (e.g. invalid channel ID), the agent must catch the error and log it to the error log table rather than silently dropping the notification.
  • Slack message blocks should include a colour-coded severity badge: green for minor, amber for moderate, red for serious, using Slack Block Kit attachment color field.
Investigation and Corrective Action Agent

This is the most complex agent in the workflow. It triggers on a confirmed incident row in Google Sheets and performs four distinct functions: creates a structured Notion investigation page with all incident fields pre-populated and a deadline set; monitors that page on a scheduled poll and sends Gmail deadline reminder emails to the assigned manager if the page status has not progressed to 'findings submitted' before the due date; once corrective actions are logged in Notion, monitors each action for completion and sends escalating Gmail reminders; and for rows where injury_involved is true, calls the BambooHR API to write the incident date, type, and reference number to the employee's record. HR retains responsibility for marking the incident formally closed in Notion, which triggers the final closure confirmation email to the reporter. Estimated build time: 11 hours. Complexity: Complex.

Trigger
New confirmed row in Google Sheets incident register (status = 'open', notification_sent = true)
Tools
Notion, Gmail, BambooHR
Replaces steps
Steps 5, 6, 7, 8, 9, and 10: investigation task creation, chasing, findings logging, corrective action tracking, completion confirmation, and BambooHR update
Estimated build
11 hours, Complex
// Input
sheets_row: {
  reference_number: string,
  severity: string,
  incident_type: string,
  incident_date: date,
  description: string,
  reporter_name: string,
  reporter_email: string,
  injury_involved: boolean,
  injured_employee_id: string | null,
  investigation_due_date: date,
  department: string
}

// Output: Notion page
notion_page: {
  database_id: [INCIDENTS_DB_ID],
  properties: {
    'Reference': reference_number,
    'Severity': severity,
    'Incident Type': incident_type,
    'Incident Date': incident_date,
    'Description': description,
    'Reporter': reporter_name,
    'Department': department,
    'Investigation Due': investigation_due_date,
    'Status': 'Under Investigation',
    'Findings': '' (blank, manager fills),
    'Corrective Actions': [] (relation, manager populates)
  }
}

// Output: Gmail reminder (deadline approaching, T-1 day)
gmail_reminder_1: {
  to: manager_email,
  subject: '[INC-XXXX] Investigation due tomorrow',
  body: 'The investigation deadline for incident [reference_number] is tomorrow. Please update the Notion page: [notion_page_url]'
}

// Output: Gmail reminder (deadline passed, T+0 and T+3 escalation)
gmail_reminder_2: {
  to: manager_email,
  cc: hr_coordinator_email,
  subject: '[INC-XXXX] Investigation overdue',
  body: 'The investigation for [reference_number] is now overdue. Please submit findings immediately.'
}

// Output: BambooHR update (injury incidents only)
bamboohr_custom_field_patch: {
  endpoint: 'PUT /v1/employees/{employee_id}/tables/customTabIncidents',
  body: {
    incident_reference: reference_number,
    incident_date: incident_date,
    incident_type: incident_type,
    outcome_notes: severity
  }
}

// On HR closes incident in Notion (status set to 'Closed')
gmail_closure: {
  to: reporter_email,
  subject: 'Incident [reference_number] closed',
  body: 'Your incident report has been investigated and all corrective actions confirmed. Reference: [reference_number].'
}
sheets_status_update: {
  row: matched by reference_number,
  status: 'closed',
  closed_at: ISO8601
}
  • The Notion incidents database ID must be provided before build. The database must already exist with the property schema listed in the IO block above. The FullSpec team will provide the exact property type definitions (select, date, text, relation) to configure before the build connection is made.
  • BambooHR API access requires an API key generated from an account with admin or full API permissions. Some BambooHR plans (Essentials tier) restrict API access entirely; this must be confirmed before build starts. If the custom incident tab does not already exist in BambooHR, it must be created by the BambooHR admin before the field mapping can be completed.
  • The BambooHR employee ID used in the API call must match the injured_employee_id field submitted on the Google Form. The form must use a field that captures a value the HR team can reliably map to BambooHR employee IDs (e.g. employee number, not a name).
  • Reminder polling must run on a scheduled trigger (e.g. once per business day at 08:00 in the business's local timezone) rather than on a time-delay from the original trigger, to avoid issues with weekend deadlines.
  • If the Notion page status is updated to 'Closed' by anyone other than the designated HR coordinator role, a guard condition should check that all corrective action sub-tasks are marked complete before the closure email fires. Partial closure must be blocked.
  • For free-text incident descriptions, a human review step for any incident auto-classified as 'serious' is strongly recommended before the Notion investigation page is created and the director notified. A short approval gate (e.g. HR coordinator confirms severity in a Slack message before the agent proceeds) should be discussed with the process owner before the serious-incident branch is built.
  • Gmail sending is performed via the Google Workspace Gmail API using a service account or OAuth credential authorised for the HR coordinator's mailbox. The scopes required are gmail.send and, for polling unread threads, gmail.readonly.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow, Workplace Incident Reporting automation
// ── TRIGGER ──────────────────────────────────────────────────────────────
// Employee submits Google Form
TRIGGER: google_forms.on_new_response(form_id: INCIDENT_FORM_ID)
  -> form_response {
       submitted_at, reporter_name, reporter_email,
       incident_date, incident_time, incident_location,
       incident_type, injury_involved, injured_employee_id,
       description, witnesses
     }

// ── AGENT 1: INCIDENT INTAKE AGENT ───────────────────────────────────────
// Input: raw form_response
// Action: apply severity classification rules
severity = classify(incident_type, injury_involved, description)
  // rule: injury_involved = true AND incident_type = 'injury' -> 'serious'
  // rule: incident_type = 'near-miss' AND injury_involved = false -> 'minor'|'moderate'
  // rule: incident_type = 'hazard-report' -> 'minor' by default

// Action: assign reference number (read last INC-XXXX from config cell, increment)
reference_number = 'INC-' + zero_pad(last_ref + 1, 4)

// Action: compute investigation_due_date
investigation_due_date = add_business_days(
  incident_date,
  severity == 'serious' ? 2 : 5
)

// Action: write to Google Sheets register
google_sheets.append_row(sheet_id: REGISTER_SHEET_ID, tab: 'Incidents', row: {
  reference_number, submitted_at, reporter_name, reporter_email,
  incident_date, incident_type, injury_involved, injured_employee_id,
  severity, description, status: 'open',
  notification_sent: false, investigation_due_date,
  corrective_actions_closed: false, bamboohr_updated: false
})

// Output: confirmed row in Google Sheets with reference_number, severity, due_date

// ── AGENT 2: NOTIFICATION AND ESCALATION AGENT ───────────────────────────
// Input: new Google Sheets row where notification_sent = false
// Trigger: polled on new row append OR event-driven via webhook from Sheets

manager_slack_id = routing_table.lookup(department)
  // routing_table: [department -> slack_channel_id | slack_user_id]
  // must be pre-configured as a static lookup in the automation platform

// Action: send manager Slack notification (all severities)
slack.post_message(channel: manager_slack_id, blocks: [
  severity_badge(severity),  // green|amber|red
  text: '[reference_number] | [incident_type] | Due: [investigation_due_date]',
  text: description[0:300]
])

// Action: send director DM (serious only)
IF severity == 'serious':
  slack.post_message(channel: OPS_DIRECTOR_SLACK_ID,
    text: 'SERIOUS: [reference_number] requires immediate awareness.'
  )

// Action: log notification and update sheet
google_sheets.update_row(reference_number, {
  notification_sent: true,
  notified_at: now()
})
google_sheets.append_row(tab: 'NotificationLog', row: {
  reference_number, notified_at, manager_slack_id,
  director_notified: severity == 'serious'
})

// Output: Slack messages delivered; notification_sent = true in register

// ── AGENT 3: INVESTIGATION AND CORRECTIVE ACTION AGENT ───────────────────
// Input: confirmed Sheets row, notification_sent = true

// Action: create Notion investigation page
notion_page_id = notion.create_page(database_id: INCIDENTS_DB_ID, properties: {
  'Reference': reference_number,
  'Severity': severity,
  'Incident Type': incident_type,
  'Incident Date': incident_date,
  'Description': description,
  'Reporter': reporter_name,
  'Department': department,
  'Investigation Due': investigation_due_date,
  'Status': 'Under Investigation'
})

// Store notion_page_id back to Sheets row for reference
google_sheets.update_row(reference_number, { notion_page_url: notion_page_id })

// ── SCHEDULED POLL: daily at 08:00 local time ────────────────────────────
// Action: check each open Notion page against today's date
FOR each open_incident IN google_sheets.get_rows(status: 'open'):
  notion_status = notion.get_page(notion_page_id).properties['Status']

  IF today == investigation_due_date - 1 AND notion_status == 'Under Investigation':
    gmail.send(to: manager_email,
      subject: '[reference_number] Investigation due tomorrow',
      body: 'Please update: [notion_page_url]'
    )

  IF today >= investigation_due_date AND notion_status == 'Under Investigation':
    gmail.send(to: manager_email, cc: hr_email,
      subject: '[reference_number] Investigation OVERDUE',
      body: 'Immediate action required.'
    )

// ── BambooHR UPDATE (injury incidents only) ───────────────────────────────
IF injury_involved == true AND bamboohr_updated == false:
  bamboohr.put('/v1/employees/{injured_employee_id}/tables/customTabIncidents', {
    incident_reference: reference_number,
    incident_date: incident_date,
    incident_type: incident_type,
    outcome_notes: severity
  })
  google_sheets.update_row(reference_number, { bamboohr_updated: true })

// ── CLOSURE TRIGGER (HR sets Notion page Status to 'Closed') ─────────────
// Trigger: Notion webhook on page property change OR daily poll detects 'Closed'
IF notion_status == 'Closed' AND corrective_actions_all_complete:
  gmail.send(to: reporter_email,
    subject: 'Incident [reference_number] closed',
    body: 'Your report has been investigated and corrective actions confirmed.'
  )
  google_sheets.update_row(reference_number, {
    status: 'closed',
    closed_at: now()
  })

// ── END OF FLOW ──────────────────────────────────────────────────────────
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool with native connectors for Google Workspace, Slack, Notion, and BambooHR. One discrete workflow per agent: Workflow 1 (Incident Intake), Workflow 2 (Notification and Escalation), Workflow 3 (Investigation and Corrective Action). All three workflows share a single credential store so that API keys and OAuth tokens are managed in one place and not duplicated across workflows.
Webhook configuration
Google Forms submission triggers Workflow 1 via the automation platform's native Google Forms event trigger or a Google Apps Script onFormSubmit webhook posting to the platform's inbound webhook URL. Workflow 2 is triggered by a row-append event on the Google Sheets register (using the platform's Sheets polling connector at a 1-minute interval, or a Sheets Apps Script onChange webhook for near-real-time delivery). Notion status-change events for Workflow 3 closure detection are handled via the Notion API polling approach (daily scheduled trigger reading page properties), as Notion webhooks are not available in the standard API at the time of build.
Templating approach
Slack Block Kit JSON templates are stored as static templates in the orchestration layer with variable interpolation for reference_number, severity, incident_type, investigation_due_date, and notion_page_url. Gmail email bodies are stored as plain-text templates with the same variable tokens. Severity-to-colour mapping (minor: good/green, moderate: warning/amber, serious: danger/red) is implemented as a lookup node in the Slack workflow before the message is dispatched.
Error logging
All agent errors (failed API calls, missing field values, unroutable departments, BambooHR 4xx/5xx responses) are written to an error log table in a dedicated Google Sheet tab named 'ErrorLog' with columns: timestamp, workflow_name, reference_number, error_code, error_message, retry_attempted. A Slack alert is posted to a designated '#automation-alerts' channel for any error that prevents a notification from being sent or a Notion page from being created. Non-critical errors (e.g. BambooHR update failure) log to the sheet and flag the bamboohr_updated field as 'failed' in the register row without halting the rest of the workflow.
Testing approach
All three agents are built and tested in sandbox mode first: a duplicate Google Form, a test Google Sheets register tab (tab name: 'Incidents-Test'), a test Notion database, a private Slack channel (#incident-test), and a BambooHR sandbox account or a test employee record agreed with the BambooHR admin. No production data is touched until end-to-end QA across all severity types (minor, moderate, serious) passes in the sandbox environment. Credential switchover from sandbox to production is the final step before go-live.
Estimated total build time
Incident Intake Agent: 6 hrs. Notification and Escalation Agent: 5 hrs. Investigation and Corrective Action Agent: 11 hrs. End-to-end integration testing and QA: 5 hrs (included in the 22-hour build estimate). Total: 22 hours across a 4-week delivery schedule.
Pre-build blockers: The following must be resolved before any build work begins. (1) Severity classification rules agreed and documented in writing by the HR manager. (2) BambooHR API access confirmed, including plan tier and whether the custom incident tab exists. (3) Google Sheets register tab and column schema created and shared with the FullSpec build account. (4) Notion incidents database created with the correct property schema. (5) Department-to-Slack-channel routing table provided. (6) Operations director Slack user ID confirmed. Delays to any of these items will extend the delivery timeline beyond 4 weeks.
For technical questions during the build or after go-live, contact the FullSpec team at support@gofullspec.com.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Workplace Incident Reporting.

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