Back to Org Chart & Headcount Management

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

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

Step
Name
Description
1
Receive Notification of Personnel Change
HR is notified via Slack or email that a hire, departure, or role change has occurred. Source is often an informal manager message rather than a system event. (5 min)
2
Update Employee Record in BambooHR
HR Manager logs into BambooHR and updates role, department, manager, start date, or termination details for the affected employee. (15 min)
3
Update Payroll Record in Gusto
HR Manager or payroll admin logs into Gusto separately to mirror the BambooHR change, including salary, cost centre, or termination status. (15 min)
4
Update Headcount Spreadsheet
The master headcount Google Sheet is opened and the relevant row is manually edited to reflect the change, including department, FTE count, and open role status. BOTTLENECK: no link to BambooHR; any discrepancy persists until the next manual review. (20 min)
5
Redraw Org Chart in Lucidchart
HR Manager opens Lucidchart and manually repositions boxes, updates names, titles, and reporting lines to reflect the change. BOTTLENECK: entirely disconnected from all other systems; diagrams go stale immediately after each change. (30 min)
6
Cross-check for Consistency
HR Manager compares BambooHR, the headcount sheet, and Lucidchart side by side to confirm all three match, correcting any discrepancies found. (20 min)
7
Notify Relevant Stakeholders
HR Manager manually sends Slack messages or emails to IT, the hiring manager, finance, and any other affected parties confirming the change. (15 min)
8
File Supporting Documentation
HR Coordinator saves offer letters, resignation letters, or approval emails to the correct Google Drive folder for the employee. (10 min)
9
Produce Weekly Headcount Report
HR Manager compiles a headcount summary from the spreadsheet at the end of each week and distributes it to leadership via email. (25 min)
Time cost summary: Each personnel change cycle costs 155 minutes (approx. 2 hrs 35 min) of manual HR time. At approximately 12 changes per month that is 31 hours of manual work per month. The weekly standing cost is 5 hours per week when the weekly report and recurring reconciliation time is included. Steps 1, 2, 3, 4, 5, 6, 7, and 9 are replaced by automation. Step 8 (document filing) and any salary-adjustment approval step remain manual by design.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Headcount Sync Agent

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.

Trigger
BambooHR webhook fires on employee record save (event types: employee.created, employee.updated, employee.terminated)
Tools
BambooHR (webhook source), Gusto (REST API write), Google Sheets (Sheets API row upsert)
Replaces steps
Steps 1, 2, 3, 4, and 6
Estimated build
14 hours — Complex
// 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.
Org Chart Update Agent

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.

Trigger
Internal event: Headcount Sync Agent emits sync_status = 'success' with a valid employee_id
Tools
Lucidchart (REST API: diagram read and shape update), BambooHR (REST API: employee detail read)
Replaces steps
Step 5
Estimated build
10 hours — Moderate
// 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.
Notification and Reporting Agent

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.

Trigger
Event: Headcount Sync Agent emits sync_status = 'success'. Schedule: Monday 08:00 local timezone (configurable) for the weekly report.
Tools
Slack (Web API: chat.postMessage), Google Sheets (Sheets API: range read), Gmail (Gmail API: message send), Google Workspace (Drive API: file read for document filing confirmation)
Replaces steps
Steps 7 and 9
Estimated build
8 hours — Simple
// 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.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow — Org Chart & Headcount Management Automation
// ─────────────────────────────────────────────────────────────────────
// 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.'
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool running one workflow per agent (three workflows total). Each workflow is independently deployable and testable. All credentials are stored in a single shared credential store accessed by all three workflows. No credentials are hardcoded in workflow logic.
Webhook configuration
BambooHR webhook registered to the orchestration layer's inbound webhook URL. The URL is generated after the orchestration environment is provisioned. The webhook must fire on employee record save for event types: employee.created, employee.updated, employee.terminated. A shared secret header (X-BambooHR-Signature) must be validated on receipt to reject unauthorised payloads.
Templating approach
Slack messages and the Gmail weekly report use server-side HTML templates stored in the orchestration layer's template store. Template variables are injected at runtime from the Google Sheets read output and the incoming sync_event payload. The Gmail report template must be approved by the HR Manager before go-live and stored as a versioned asset.
Error logging
All agent failures after three retries are written to a Supabase table named 'automation_errors' with columns: id (uuid), agent_name (text), employee_id (text), event_type (text), error_message (text), retry_count (integer), resolved (boolean), created_at (timestamptz). A Slack alert is sent to #it-ops on each new error row insert. The FullSpec team will provision the Supabase project and share read access with the process owner.
Testing approach
All agents are built and tested against sandbox or staging credentials first. BambooHR sandbox: use a test employee account with a dedicated employee_id not present in production. Gusto sandbox: Gusto provides a demo company environment for API testing. Lucidchart: use a duplicate of the production org chart document with a distinct document_id. Google Sheets: use a dedicated test spreadsheet with the same schema as production. No test data is ever written to production systems until UAT sign-off.
Credential store entries
BAMBOOHR_API_KEY, BAMBOOHR_SUBDOMAIN, BAMBOOHR_WEBHOOK_SECRET, GUSTO_CLIENT_ID, GUSTO_CLIENT_SECRET, GUSTO_ACCESS_TOKEN, GUSTO_REFRESH_TOKEN, GOOGLE_SERVICE_ACCOUNT_JSON, HEADCOUNT_SHEET_ID, HEADCOUNT_TAB_NAME, LUCIDCHART_CLIENT_ID, LUCIDCHART_CLIENT_SECRET, LUCIDCHART_ACCESS_TOKEN, LUCIDCHART_DOCUMENT_ID, SLACK_BOT_TOKEN, SLACK_CHANNEL_MAP_JSON, GMAIL_SENDER_ADDRESS, LEADERSHIP_EMAIL_LIST, SALARY_THRESHOLD_USD, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY
Estimated total build time
Headcount Sync Agent: 14 hrs. Org Chart Update Agent: 10 hrs. Notification and Reporting Agent: 8 hrs. End-to-end integration testing and UAT support: 8 hrs (included in snapshot build_effort_hours). Total: 40 hrs across a 4-week delivery schedule.
Discovery and data mapping (Week 1) must be completed before any agent build begins. The BambooHR field map, Gusto OAuth application registration, headcount sheet schema lock, Lucidchart shape enrichment exercise, and salary threshold confirmation are all hard dependencies for the Headcount Sync Agent. Starting Agent 1 build without these confirmed will require rework. The FullSpec team will conduct a structured discovery session at the start of Week 1 to gather all required inputs. Contact the FullSpec team at support@gofullspec.com with any pre-build questions.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Org Chart & Headcount Management.

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