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
Org Chart & Headcount Management Automation
[YourCompany.com] · HR Department · Prepared by FullSpec · [Today's Date]
This document is the primary technical reference for the FullSpec team building the Org Chart and Headcount Management automation. It covers the current-state process map, full agent specifications with IO contracts, the end-to-end data flow trace, and the recommended build stack. Everything required to begin building, wiring, and testing all three agents is contained here. The process owner's responsibilities are limited to confirming API credentials and approving UAT results. All build, integration, and deployment work is handled by the FullSpec team.
01Process snapshot
02Agent specifications
Monitors BambooHR for personnel change webhook events (new hire, role change, and termination) and propagates the confirmed data to Gusto and the master headcount Google Sheet without any human intervention. This agent is the entry point for the entire automation chain. All downstream agents depend on a confirmed write from this agent before they execute. A salary-adjustment threshold check is embedded as a decision branch: changes above the configured dollar threshold are routed to an HR approval step before any write to Gusto occurs.
// Input (BambooHR webhook payload) event_type : 'employee.created' | 'employee.updated' | 'employee.terminated' employee_id : string // BambooHR internal ID first_name : string last_name : string job_title : string department : string reports_to_id : string // manager's BambooHR ID employment_status : 'Active' | 'Terminated' effective_date : ISO8601 date string salary : number // USD, present on updated events only cost_centre : string // Decision branch IF salary_delta > threshold THEN route_to_hr_approval() ELSE proceed_to_gusto_write() // Output (on successful sync) gusto_employee_id : string // echoed back for downstream agents sheet_row_index : integer // row updated or inserted in headcount sheet sync_status : 'success' | 'pending_approval' | 'error' sync_timestamp : ISO8601 datetime change_summary : string // human-readable one-liner for notification agent // On approval (salary threshold path) approval_required : true approval_sent_to : string // HR Manager email approval_token : string // unique ID to resume workflow on approval
- BambooHR webhook endpoint must be registered under Settings > API > Webhooks on the BambooHR account. Confirm the account is on a tier that exposes webhook configuration (Essentials or above). The FullSpec team will supply the target URL after the orchestration layer is provisioned.
- Gusto API requires OAuth 2.0 with the employees:write and payroll:write scopes. The client must be registered as a Gusto partner application; a direct API key is not sufficient for write operations on compensation records.
- Google Sheets connection requires a service account with Editor access granted to the specific headcount spreadsheet. The sheet ID and the exact tab name must be confirmed during the discovery and data mapping stage (Week 1) before build begins.
- Salary adjustment threshold: the dollar value above which Gusto writes are gated must be agreed and hardcoded in the workflow config before go-live. Default assumption is $5,000 delta; this must be confirmed by the HR Manager.
- Dedupe logic: if two webhook events fire within 60 seconds for the same employee_id, the agent must process only the latest payload and discard the earlier one. Implement using a keyed lock on employee_id in the orchestration layer's state store.
- Fallback behaviour: if the Gusto write fails after three retries, the agent must write an error row to the error log table, pause the chain, and send a Slack alert to the HR Manager. The Google Sheets write must not be blocked by a Gusto failure.
- The headcount sheet schema must be locked before build. Confirm column order: employee_id, first_name, last_name, job_title, department, reports_to, employment_status, fte_status, effective_date, cost_centre, last_updated_by.
Listens for a confirmed sync event from the Headcount Sync Agent and uses the Lucidchart API to update the relevant diagram node. It reads current employee data directly from BambooHR to ensure the diagram reflects the authoritative record, then rewrites the affected shape's label, title, department, and parent connector to match the new reporting line. The agent does not create new diagram pages; it operates only on a pre-structured template with consistent shape naming conventions agreed during the discovery phase.
// Input (from Headcount Sync Agent output)
employee_id : string
event_type : 'employee.created' | 'employee.updated' | 'employee.terminated'
job_title : string
department : string
reports_to_id : string
effective_date : ISO8601 date string
// Intermediate: BambooHR GET /v1/employees/{employee_id}
full_name : string // first_name + last_name
manager_full_name : string // resolved from reports_to_id
// Lucidchart shape operations
document_id : string // Lucidchart org chart document ID (hardcoded in config)
shape_id : string // matched by custom data field 'bamboohr_id' on each shape
operation : 'update_label' | 'update_parent' | 'delete_shape' | 'create_shape'
// Output
lucidchart_doc_id : string
shape_updated : boolean
diagram_version : integer // Lucidchart document version after write
update_status : 'success' | 'shape_not_found' | 'error'
update_timestamp : ISO8601 datetime- Lucidchart API access requires a Team or Enterprise plan. The account holder must generate an OAuth 2.0 application under Lucidchart Developer Settings and share the client_id and client_secret with the FullSpec team before the Week 3 build stage begins.
- Every shape in the Lucidchart org chart must have a custom data field named 'bamboohr_id' set to the corresponding BambooHR employee_id before automation goes live. If the existing diagram was built ad hoc without this field, a one-time data enrichment exercise is required. The FullSpec team will provide a shape-ID export script to assist.
- For employee.created events, the agent will attempt to match the manager's shape by reports_to_id. If the manager shape is not found, the agent must create the new employee shape in an 'Unlinked' holding layer and fire a Slack alert to the HR Manager to manually connect the reporting line.
- For employee.terminated events, the shape must be removed from the active diagram layer and archived to a hidden 'Former Employees' layer rather than deleted, to preserve audit history.
- Rate limit: Lucidchart's API allows 60 requests per minute per OAuth token. For bulk restructure events (more than 10 employees changed in a single BambooHR batch save), the agent must implement a 1-second delay between shape operations.
- The Lucidchart document_id must be confirmed and hardcoded in the workflow configuration during Week 3. If the organisation maintains multiple regional org chart documents, each requires a separate document_id mapping keyed by department.
Handles all downstream communication triggered by a confirmed personnel change or by a Monday morning schedule. On each change event, it sends a formatted Slack message to the relevant channels (IT, finance, and the hiring manager's channel) summarising the change and effective date. On a Monday 08:00 schedule, it pulls live headcount totals from the Google Sheet, formats a leadership summary, and delivers it via Gmail to the leadership distribution list. This agent has no write access to HR or payroll systems and operates read-only on Google Sheets.
// Input A: change-event path
employee_id : string
full_name : string
event_type : 'employee.created' | 'employee.updated' | 'employee.terminated'
job_title : string
department : string
effective_date : ISO8601 date string
change_summary : string // passed from Headcount Sync Agent
// Input B: weekly report schedule path
trigger_type : 'schedule'
report_date : ISO8601 date string // Monday date
// Intermediate: Google Sheets read
total_headcount : integer
headcount_by_dept : object // { department: count }
open_roles_count : integer // rows where employment_status = 'Open'
changes_this_week : integer // rows where effective_date >= Monday of current week
// Output A: Slack notification
slack_channel_ids : string[] // e.g. ['#it-ops', '#finance', '#people']
slack_message_ts : string // Slack message timestamp on success
notification_status: 'sent' | 'error'
// Output B: Gmail weekly report
email_recipients : string[] // leadership distribution list
email_subject : string // 'Headcount Report — {report_date}'
email_body_format : 'HTML'
send_status : 'sent' | 'error'- Slack Web API requires a bot token (xoxb-) with the channels:read, chat:write, and users:read OAuth scopes. The Slack app must be installed to the workspace and invited to each target channel before go-live. Confirm exact channel IDs (not names) during Week 4 build.
- Gmail send requires a Google service account with domain-wide delegation enabled for the HR Manager's Google Workspace account, with the gmail.send OAuth scope. Alternatively, a dedicated automation Gmail address (e.g. hr-automation@[YourCompany.com]) can be used if domain-wide delegation is not available.
- The weekly report email template must be HTML. The FullSpec team will build a fixed template with dynamic headcount fields injected at send time. The HR Manager must approve the template layout before go-live.
- The leadership distribution list (email addresses for the weekly report) must be provided in writing by the process owner before the Week 4 build stage. Store it as a comma-separated config variable in the orchestration layer's credential store.
- The Slack routing logic must map department values from BambooHR to Slack channel IDs. This mapping table must be agreed and stored in the workflow config. If a department has no mapped channel, the agent defaults to #people-ops and logs a warning.
- No sensitive salary data must appear in Slack messages or email bodies. The change_summary field passed from the Headcount Sync Agent must be pre-sanitised to exclude salary figures before it reaches this agent.
03End-to-end data flow
// ─────────────────────────────────────────────────────────────────────
// TRIGGER: BambooHR Personnel Change Event
// ─────────────────────────────────────────────────────────────────────
BambooHR.webhook ->
event_type : 'employee.created' | 'employee.updated' | 'employee.terminated'
employee_id : '12345'
first_name : 'Jane'
last_name : 'Doe'
job_title : 'Senior Account Manager'
department : 'Sales'
reports_to_id : '98701'
employment_status : 'Active'
effective_date : '2025-05-05'
salary : 72000
cost_centre : 'CC-SALES-01'
// ─────────────────────────────────────────────────────────────────────
// AGENT 1: Headcount Sync Agent
// ─────────────────────────────────────────────────────────────────────
// Step 1: Salary threshold check
salary_delta = abs(salary - previous_salary) // fetched via BambooHR GET /v1/employees/{id}
IF salary_delta > threshold (e.g. 5000)
-> route_to_hr_approval(approval_token, hr_manager_email)
-> PAUSE until approval_token confirmed
ELSE
-> proceed
// Step 2: Write to Gusto
Gusto.PUT /v1/employees/{gusto_employee_id}/jobs
body ->
title : job_title
department : department
compensation : salary // only if below threshold or approved
effective_date : effective_date
response ->
gusto_employee_id: '67890'
gusto_write_status: 'success'
// Step 3: Upsert headcount Google Sheet row
GoogleSheets.UPSERT tab='Headcount' match_column='employee_id'
row ->
employee_id : '12345'
first_name : 'Jane'
last_name : 'Doe'
job_title : 'Senior Account Manager'
department : 'Sales'
reports_to : '98701'
employment_status: 'Active'
fte_status : 'FTE'
effective_date : '2025-05-05'
cost_centre : 'CC-SALES-01'
last_updated_by : 'automation'
response ->
sheet_row_index : 47
// Agent 1 emits downstream event:
sync_event ->
employee_id : '12345'
event_type : 'employee.updated'
sync_status : 'success'
sync_timestamp : '2025-05-05T09:02:14Z'
change_summary : 'Jane Doe promoted to Senior Account Manager in Sales, effective 2025-05-05'
sheet_row_index : 47
gusto_employee_id : '67890'
// ─────────────────────────────────────────────────────────────────────
// AGENT 2: Org Chart Update Agent
// Trigger: sync_event.sync_status == 'success'
// ─────────────────────────────────────────────────────────────────────
// Step 4: Resolve manager name from BambooHR
BambooHR.GET /v1/employees/98701?fields=firstName,lastName
response ->
manager_full_name: 'Tom Ashby'
// Step 5: Find Lucidchart shape by custom data field
Lucidchart.GET /documents/{document_id}/contents
filter: customData.bamboohr_id == '12345'
response ->
shape_id : 'shape-441'
current_label : 'Jane Doe\nAccount Manager'
// Step 6: Update Lucidchart shape
Lucidchart.PATCH /documents/{document_id}/contents/shapes/{shape_id}
body ->
label : 'Jane Doe\nSenior Account Manager'
parent_shape_id : {shape matched by bamboohr_id == '98701'}
department_tag : 'Sales'
response ->
shape_updated : true
diagram_version : 214
// Agent 2 completes. No downstream event emitted (terminal for this chain branch).
// ─────────────────────────────────────────────────────────────────────
// AGENT 3A: Notification and Reporting Agent — Change Event Path
// Trigger: sync_event.sync_status == 'success' (parallel to Agent 2)
// ─────────────────────────────────────────────────────────────────────
// Step 7: Post Slack notification
Slack.chat.postMessage
channel : '#it-ops' // mapped from department = 'Sales'
text : 'Personnel update: Jane Doe promoted to Senior Account Manager
in Sales, effective 2025-05-05. BambooHR and Gusto updated.'
response ->
slack_message_ts : '1746432134.000200'
notification_status: 'sent'
// ─────────────────────────────────────────────────────────────────────
// AGENT 3B: Notification and Reporting Agent — Weekly Schedule Path
// Trigger: cron Monday 08:00
// ─────────────────────────────────────────────────────────────────────
// Step 8: Read headcount summary from Google Sheet
GoogleSheets.GET tab='Headcount' range='A:K'
computed ->
total_headcount : 87
headcount_by_dept: { Sales: 22, Engineering: 31, Operations: 18, HR: 5, Finance: 11 }
open_roles_count : 4
changes_this_week: 3
// Step 9: Send weekly report via Gmail
Gmail.send
from : 'hr-automation@[YourCompany.com]'
to : ['ceo@[YourCompany.com]', 'cfo@[YourCompany.com]', ...]
subject : 'Headcount Report — 2025-05-05'
body_format : 'HTML'
body_fields : { total_headcount, headcount_by_dept, open_roles_count, changes_this_week }
response ->
send_status : 'sent'
// ─────────────────────────────────────────────────────────────────────
// ERROR PATH (any agent)
// ─────────────────────────────────────────────────────────────────────
ON error after 3 retries ->
Supabase.INSERT table='automation_errors'
{ agent_name, employee_id, event_type, error_message, timestamp, retry_count }
Slack.chat.postMessage channel='#it-ops' text='[AUTOMATION ALERT] {agent_name} failed
for employee {employee_id}. Check error log.'04Recommended build stack
More documents for this process
Every document generated for Org Chart & Headcount Management.