Back to Leave & Absence 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

Leave and Absence Management

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

This document is the primary technical reference for the FullSpec team building the Leave and Absence Management automation. It covers the full current-state process map with bottleneck identification, all three agent specifications with IO contracts, the end-to-end data flow trace, and the recommended build stack. Everything the FullSpec team needs to begin the build without ambiguity is contained here. Where a decision or prerequisite remains open, it is called out explicitly so it can be resolved before build starts.

01Process snapshot

Step
Name
Description
1
Employee Submits Leave Request
Employee emails HR or sends a Slack message with dates, leave type, and notes. No standard form means submissions are regularly incomplete. Time cost: 10 min.
2
HR Logs Request in Spreadsheet
HR opens the Google Sheets leave tracker and manually enters employee name, dates, leave type, and current balance. Errors here cascade into every downstream step. Time cost: 15 min.
3
HR Checks Leave Balance
HR cross-references the spreadsheet against BambooHR to confirm the employee holds sufficient entitlement for the requested period. Time cost: 10 min.
4
Check Team Calendar for Coverage Conflicts
HR or the line manager manually reviews Google Calendar to identify overlapping approved absences. This step has no time constraint and is a regular source of delay. Time cost: 15 min.
5
Forward Request to Line Manager for Approval
HR emails the line manager the request details and asks for a decision by reply. No deadline is set, so responses routinely take two or more days. Time cost: 10 min.
6
Manager Reviews and Responds
The manager replies by email to approve or decline, sometimes requesting a date change. HR must monitor the inbox to catch the reply. Time cost: 20 min.
7
HR Notifies Employee of Decision
HR composes and sends an individual email to the employee confirming the outcome and, if approved, the dates. Time cost: 10 min.
8
Update Leave Balance in Spreadsheet and BambooHR
HR deducts approved days from the employee record in both the spreadsheet and BambooHR. Drift between the two systems causes payroll discrepancies. Time cost: 15 min.
9
Add Approved Leave to Google Calendar
HR manually creates a calendar event in the shared team calendar so colleagues can see the absence. Time cost: 8 min.
10
Notify Team in Slack
HR or the manager posts a message in the relevant Slack channel to alert the team to the upcoming absence. Time cost: 5 min.
11
Update Payroll System for Leave Deductions
HR logs into Gusto and records the approved leave so payroll adjustments are captured before the next pay run closes. Time cost: 12 min.
Time cost summary: Each leave request cycle consumes 130 minutes of manual time across 11 steps. At approximately 40 requests per month the process absorbs roughly 87 hours of manual effort per month, or 6 hours per week. Steps 2, 3, 4, 7, 8, 9, 10, and 11 are fully replaced by the automation. Steps 4 and 5 are the primary bottlenecks, together accounting for the 2 to 3 day approval delay. Step 6 (manager decision) is intentionally retained as a human step. Step 1 is replaced by a structured form trigger.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Leave Intake and Balance Agent

Receives a structured leave request from the submission form trigger, queries BambooHR via its REST API to retrieve the employee's current leave balance and entitlement for the relevant leave type, then scans the shared team Google Calendar for any overlapping approved absences during the requested date range. If the balance is sufficient and no blocking conflict exists, the agent packages a validated request summary and passes it to the Approval Routing Agent. If the balance is insufficient or a hard conflict is detected, the agent routes to a decline notification via Gmail and terminates the workflow run. Estimated build time: 8 hours. Complexity: Moderate.

Trigger
New leave request form submission; structured payload containing employee_id, leave_type, start_date, end_date, and optional notes fields.
Tools
BambooHR (REST API, GET /employees/{id}/timeOffBalances and POST /time_off/requests), Google Calendar (API v3, Events.list for conflict scan).
Replaces steps
Steps 2, 3, and 4: HR logging in Google Sheets, balance check in BambooHR, and manual calendar conflict review.
Estimated build
8 hours, Moderate complexity.
// Input
employee_id: string          // BambooHR employee ID
leave_type: string           // e.g. 'annual', 'sick', 'unpaid', 'parental'
start_date: date (ISO 8601)  // e.g. 2025-05-12
end_date: date (ISO 8601)    // e.g. 2025-05-16
requested_days: integer      // calendar working days in range
notes: string | null         // optional employee comment

// Output (on pass)
request_id: string           // UUID generated at intake
employee_name: string        // resolved from BambooHR record
leave_balance_remaining: float  // days available before this request
balance_sufficient: boolean  // true if remaining >= requested_days
conflict_detected: boolean   // true if overlapping events found in Calendar
conflict_details: array | null  // list of conflicting event summaries
status: 'ready_for_approval' | 'declined_balance' | 'declined_conflict'

// On decline (balance or conflict fail)
decline_reason: string       // human-readable reason for Gmail notification
employee_email: string       // resolved from BambooHR for fallback Gmail send
  • BambooHR API access must be enabled at the account plan level before build starts. Confirm with the client that the plan is Standard or above, as the Essentials tier does not expose the Time Off API endpoints required for balance retrieval and request logging.
  • The Google Calendar conflict check must target the correct shared calendar. Confirm the calendar ID (typically a long string ending in @group.calendar.google.com) and ensure the service account or OAuth credential has at least 'Reader' scope on that calendar.
  • The definition of a 'blocking conflict' must be agreed with HR before the logic is coded. Options include: any overlap with another absence, overlap only if the same team is under minimum headcount, or a percentage threshold. Leave this as a configurable parameter, not a hardcoded rule.
  • Dedupe behaviour: if a request with an identical employee_id, leave_type, start_date, and end_date is submitted within 10 minutes of a prior submission, the second run must be suppressed and a duplicate-flag logged to the error table. Do not process it twice.
  • If BambooHR returns a non-200 response, the agent must pause, log the error with request_id and HTTP status to the Supabase error table, and send an alert to the FullSpec support channel before retrying once after 5 minutes.
Approval Routing Agent

Receives the validated request summary from the Leave Intake and Balance Agent and sends a structured approval message to the line manager via Slack, using a custom Slack app with interactive components (approve and decline buttons). The message includes employee name, leave type, dates, days requested, remaining balance after approval, and any conflict advisory notes. The agent then enters a wait state listening for the manager's button response. On response, it logs the decision with a timestamp and the manager's Slack user ID for audit purposes, then routes the outcome: approved requests proceed to the Post-Approval Sync Agent, and declined requests trigger a Gmail notification to the employee with the manager's decision. Estimated build time: 10 hours. Complexity: Moderate.

Trigger
Leave Intake and Balance Agent emits status: 'ready_for_approval' and passes the validated request payload.
Tools
Slack (custom app with bot scopes: chat:write, im:write, users:read.email; interactive components for approve/decline buttons), Gmail (SMTP or Gmail API send-only, fallback notification on decline path).
Replaces steps
Steps 5, 6 routing mechanics, and 7: forwarding request to manager by email, routing the manager response, and notifying the employee of the decision.
Estimated build
10 hours, Moderate complexity.
// Input (from Leave Intake and Balance Agent)
request_id: string
employee_name: string
employee_email: string
employee_slack_id: string    // resolved via Slack users.lookupByEmail
manager_slack_id: string     // resolved from BambooHR supervisor field
leave_type: string
start_date: date
end_date: date
requested_days: integer
leave_balance_remaining: float
conflict_details: array | null

// Output (on approval)
decision: 'approved'
decided_by_slack_id: string  // Slack user ID of responding manager
decided_at: timestamp (ISO 8601)
request_id: string           // passed through for audit chain

// Output (on decline)
decision: 'declined'
decline_note: string | null  // optional manager comment from Slack modal
decided_by_slack_id: string
decided_at: timestamp (ISO 8601)

// On approval: passes full payload to Post-Approval Sync Agent
// On decline: triggers Gmail send to employee_email with decline_note
  • A custom Slack app must be created in the target Slack workspace before this agent can be built. Required bot token scopes: chat:write, im:write, users:read, users:read.email, and app_mentions:read. The interactive components endpoint (the webhook URL that receives button clicks) must be registered in the Slack app manifest under Interactivity.
  • The manager's Slack user ID must be resolved dynamically. The recommended approach is to read the supervisor email from BambooHR and call Slack's users.lookupByEmail method at runtime. Do not hardcode manager IDs as they change when org structure changes.
  • The approval wait state has a maximum timeout of 72 hours. If no response is received within 72 hours, the agent must send a reminder to the manager, wait an additional 24 hours, and if still no response, escalate by notifying the HR Manager via Slack and logging the request as 'pending_escalation' in the Supabase tracking table.
  • The Slack interactive message must not allow the same button to be clicked twice. Implement idempotency by storing the request_id against the responding Slack user ID immediately on first click and rejecting any duplicate callback with the same request_id.
  • Gmail is used as the fallback notification channel on decline only. Confirm that a dedicated sending address (e.g. hr-noreply@[YourCompany.com]) is available and that an OAuth 2.0 credential with the Gmail API scope gmail.send has been provisioned for it.
  • Leave step 6 (manager reviewing and deciding) as a fully human action. The automation surfaces information and waits; it does not auto-approve under any condition unless the client explicitly requests that rule and HR formally signs off on it.
Post-Approval Sync Agent

Triggered immediately upon receiving an approved decision from the Approval Routing Agent, this agent executes four sequential write operations across BambooHR, Google Calendar, Gusto, and Slack. It deducts the approved days from the employee's leave balance in BambooHR, creates a dated absence event in the shared team Google Calendar, logs the approved leave period in Gusto for payroll processing, and finally sends a direct Slack message to the employee confirming the approval and a channel post to the team calendar channel. All four writes are logged with request_id and timestamp. Estimated build time: 10 hours. Complexity: Moderate.

Trigger
Approval Routing Agent emits decision: 'approved' and passes the full request payload plus audit metadata.
Tools
BambooHR (REST API, PUT /employees/{id}/timeOffBalances or POST /time_off/requests with status update), Google Calendar (API v3, Events.insert on shared team calendar), Gusto (REST API, POST /v1/companies/{company_id}/time_off_requests or leave deduction endpoint), Slack (chat:write for DM to employee and channel post to team channel).
Replaces steps
Steps 8, 9, 10, and 11: updating BambooHR balance, adding the Google Calendar event, notifying the team in Slack, and logging leave in Gusto for payroll.
Estimated build
10 hours, Moderate complexity.
// Input (from Approval Routing Agent)
request_id: string
employee_id: string
employee_name: string
employee_slack_id: string
employee_email: string
leave_type: string
start_date: date
end_date: date
requested_days: integer
decided_by_slack_id: string
decided_at: timestamp
team_slack_channel_id: string  // resolved from config or HR settings table

// Output
bamboohr_updated: boolean
calendar_event_id: string    // Google Calendar event ID for reference
gusto_log_id: string         // Gusto leave record ID
employee_notified: boolean
team_notified: boolean
sync_completed_at: timestamp

// On partial failure
failed_steps: array          // e.g. ['gusto_log'] if Gusto write fails
error_detail: string         // logged to Supabase error table with request_id
  • Gusto API access requires a company-level OAuth token. Confirm whether the Gusto account is on a plan that exposes the Time Off API. Gusto's partner API and direct API differ in available endpoints; clarify which credential type the client holds before building this step.
  • The four write operations (BambooHR, Google Calendar, Gusto, Slack) must be executed in order. If any of the first three fails, the agent must log the failure and halt rather than skip ahead, so partial syncs do not create inconsistent records across systems.
  • BambooHR balance deduction: use the approved leave request flow (update request status to 'approved') rather than directly patching the balance field, so BambooHR's own audit trail reflects the change correctly.
  • Google Calendar event naming convention must be confirmed with HR before build. A suggested format is: '[Employee Name] - [Leave Type]' with the description field containing the request_id for traceability.
  • The team Slack channel ID must be stored in a configuration table or environment variable, not hardcoded. Different teams may use different channels; if the client has multiple departments, this must support a channel lookup by department.
  • All four sync results must be written back to the Supabase tracking table against the request_id so HR can audit any request end-to-end without accessing the individual SaaS tools.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow trace, Leave and Absence Management automation
// ─────────────────────────────────────────────────────────────
// TRIGGER: Employee submits leave request form
// ─────────────────────────────────────────────────────────────
FORM_SUBMISSION received
  -> employee_id: string         // e.g. 'EMP-00142'
  -> leave_type: string          // e.g. 'annual'
  -> start_date: '2025-06-02'    // ISO 8601
  -> end_date: '2025-06-06'      // ISO 8601
  -> requested_days: 5
  -> notes: string | null

// ─────────────────────────────────────────────────────────────
// AGENT 1: Leave Intake and Balance Agent
// ─────────────────────────────────────────────────────────────
STEP 1.1: Dedupe check
  -> query Supabase requests table WHERE employee_id + leave_type + start_date + end_date
  -> IF duplicate within 10 min: log, suppress, HALT

STEP 1.2: BambooHR balance query
  GET /employees/{employee_id}/timeOffBalances
  -> response.timeOffTypes[leave_type].balance: float   // e.g. 12.0 days
  -> employee_name: string                              // from employee record
  -> employee_email: string                             // from employee record
  -> manager_email: string                              // from supervisor field
  -> balance_sufficient: (balance >= requested_days)   // boolean

STEP 1.3: Google Calendar conflict scan
  GET /calendars/{team_calendar_id}/events
    ?timeMin=start_date&timeMax=end_date&singleEvents=true
  -> conflict_detected: boolean
  -> conflict_details: [{ summary, start, end, organizer }] | null

STEP 1.4: Route decision
  IF balance_sufficient == false:
    status = 'declined_balance'
    -> Gmail: send decline notification to employee_email
    -> Supabase: log { request_id, status, decline_reason, timestamp }
    -> HALT
  IF conflict_detected == true AND conflict_blocks == true:
    status = 'declined_conflict'
    -> Gmail: send decline notification to employee_email
    -> Supabase: log { request_id, status, decline_reason, timestamp }
    -> HALT
  ELSE:
    status = 'ready_for_approval'
    -> pass full payload to Agent 2

// ─────────────────────────────────────────────────────────────
// HANDOFF: Agent 1 -> Agent 2 (Approval Routing Agent)
// Fields passed: request_id, employee_name, employee_email,
// employee_slack_id, manager_slack_id, leave_type, start_date,
// end_date, requested_days, leave_balance_remaining,
// conflict_details
// ─────────────────────────────────────────────────────────────

// ─────────────────────────────────────────────────────────────
// AGENT 2: Approval Routing Agent
// ─────────────────────────────────────────────────────────────
STEP 2.1: Resolve Slack user IDs
  GET /slack/users.lookupByEmail?email=employee_email
  -> employee_slack_id: string
  GET /slack/users.lookupByEmail?email=manager_email
  -> manager_slack_id: string

STEP 2.2: Send Slack approval message to manager
  POST /slack/chat.postMessage
    channel: manager_slack_id
    blocks: [{
      employee_name, leave_type, start_date, end_date,
      requested_days, balance_after_approval,
      conflict_advisory (if conflict_detected but not blocking),
      action_buttons: ['Approve', 'Decline']
    }]
  -> message_ts: string          // Slack message timestamp, stored for idempotency

STEP 2.3: Wait for interactive callback (max 72 hours)
  CALLBACK received:
    -> action_id: 'approve' | 'decline'
    -> responding_user_slack_id: string
    -> callback_timestamp: ISO 8601
  IF no callback after 72 hours: send reminder, wait 24 hours, then escalate to HR Manager

STEP 2.4: Log decision
  -> Supabase: { request_id, decision, decided_by_slack_id, decided_at }

STEP 2.5: Route outcome
  IF decision == 'approved':
    -> pass full payload + audit fields to Agent 3
  IF decision == 'declined':
    -> Gmail: send decline email to employee_email with decline_note
    -> Supabase: update request status to 'declined_by_manager'
    -> HALT

// ─────────────────────────────────────────────────────────────
// HANDOFF: Agent 2 -> Agent 3 (Post-Approval Sync Agent)
// Fields passed: all Agent 1 fields + decision, decided_by_slack_id,
// decided_at, employee_slack_id, team_slack_channel_id
// ─────────────────────────────────────────────────────────────

// ─────────────────────────────────────────────────────────────
// AGENT 3: Post-Approval Sync Agent
// ─────────────────────────────────────────────────────────────
STEP 3.1: Update BambooHR leave record
  POST /time_off/requests/{request_id}/approve
    OR PUT /employees/{employee_id}/timeOffRequests with status='approved'
  -> bamboohr_updated: boolean
  -> new_balance: float          // balance after deduction

STEP 3.2: Create Google Calendar event
  POST /calendars/{team_calendar_id}/events
    summary: '{employee_name} - {leave_type}'
    start: { date: start_date }
    end: { date: end_date }
    description: 'request_id: {request_id}'
  -> calendar_event_id: string

STEP 3.3: Log leave in Gusto
  POST /v1/companies/{company_id}/time_off_requests
    employee_id: {gusto_employee_id}
    leave_type: {leave_type}
    start_date: {start_date}
    end_date: {end_date}
    days: {requested_days}
  -> gusto_log_id: string

STEP 3.4: Send Slack notifications
  DM to employee_slack_id: approval confirmed, dates, remaining balance
  POST to team_slack_channel_id: '{employee_name} is on {leave_type} {start_date} to {end_date}'
  -> employee_notified: true
  -> team_notified: true

STEP 3.5: Final audit log
  -> Supabase: { request_id, bamboohr_updated, calendar_event_id,
     gusto_log_id, employee_notified, team_notified, sync_completed_at }

// ─────────────────────────────────────────────────────────────
// END OF WORKFLOW RUN
// Total automated steps: 10 of 11 original manual steps
// Human step retained: Manager approve/decline decision (Step 2.3)
// ─────────────────────────────────────────────────────────────
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation platform (no specific tool decided at this stage). Implement one workflow per agent: 'Leave_Intake_Balance', 'Approval_Routing', 'Post_Approval_Sync'. All three workflows share a single credential store and a common environment variable set so credentials are never duplicated across workflows. Inter-workflow handoffs are handled via internal webhook calls or a shared queue, with request_id as the correlation key throughout.
Webhook configuration
The form submission trigger uses an inbound webhook endpoint generated by the automation platform. The URL is registered as the form's action endpoint. The Slack interactive components callback (approve/decline button responses) uses a second inbound webhook registered in the Slack app manifest under Interactivity and Shortcuts. Both webhook endpoints must be protected with a shared secret or HMAC signature verification. Do not expose unauthenticated endpoints.
Templating approach
All Slack messages and Gmail emails use template strings with named variable substitution (e.g. {{employee_name}}, {{leave_type}}, {{start_date}}). Templates are stored as environment variables or a config table rather than inline string literals, so HR can update message wording without touching workflow code. Slack messages use Block Kit JSON for structured layout with approve/decline action buttons.
Error logging
All workflow errors write to a Supabase table named 'leave_automation_errors' with columns: id (UUID), request_id (string), agent_name (string), error_step (string), http_status (integer | null), error_message (text), created_at (timestamp). A Supabase database webhook or scheduled function monitors this table and sends an alert to support@gofullspec.com and the designated HR Manager Slack DM whenever a new error row is inserted. Retry logic: one automatic retry after 5 minutes for transient HTTP errors (429, 503). Permanent failures (400, 401, 403) do not retry and escalate immediately.
Testing approach
All agent builds are tested against sandbox or staging credentials before any production credentials are connected. BambooHR provides a sandbox environment on Standard plans; use it throughout build and QA. Gusto's developer sandbox is available via the Gusto Developer Portal and must be provisioned before the Post-Approval Sync Agent build begins. Slack testing uses a dedicated test workspace. Google Calendar testing uses a non-production calendar shared only with the FullSpec team. Production credentials are connected only after the full end-to-end QA pass is signed off.
Estimated total build time
Leave Intake and Balance Agent: 8 hours. Approval Routing Agent: 10 hours. Post-Approval Sync Agent: 10 hours. End-to-end integration testing and QA: 5 hours. Discovery, credential setup, and environment configuration: 5 hours (per delivery stage 1). Total: 38 hours across a 4-week delivery window (noting the template records 28 hours for agent build effort; the additional 10 hours covers environment setup and end-to-end QA as separate delivery activities).
Before build starts, the following must be confirmed: (1) BambooHR plan tier supports the Time Off API. (2) Gusto API credential type (partner OAuth or direct API) is established. (3) The Slack custom app has been created in the production workspace and bot scopes approved by the workspace admin. (4) HR has documented all leave types, approval hierarchies, blackout periods, and the definition of a 'blocking conflict' for the calendar check. (5) The shared Google Calendar ID for the team absence calendar has been provided. None of these are technical blockers FullSpec introduces; they are account-level and policy prerequisites that must be resolved by your team before the build begins. Raise any of these via support@gofullspec.com as soon as they are confirmed.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Leave & Absence 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