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
Policy Acknowledgement Tracking
[YourCompany.com] · HR Department · Prepared by FullSpec · [Today's Date]
This document is the primary technical reference for the FullSpec team building the Policy Acknowledgement Tracking automation. It covers the current-state process, all three agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. Everything needed to begin implementation is contained here. Where tool credentials and template IDs are still being confirmed, the relevant prerequisite is called out explicitly so that build work is not blocked by a missing dependency.
01Process snapshot
02Agent specifications
Handles the full send-out cycle for a new or updated policy. On trigger, it queries BambooHR for the correct employee list, seeds a new tracking log in Google Sheets, and creates and dispatches DocuSign envelopes programmatically using a pre-approved template. It ensures every active employee in scope receives the correct policy version without any manual envelope setup. Estimated build: 8 hours. Complexity: Moderate.
// Input
policy_id: string // BambooHR custom field or document ID
policy_name: string // Human-readable label, used in envelope subject and Sheet header
policy_version: string // Version tag, e.g. 'v2.3', locked to envelope template revision
policy_document_url: string // Signed S3 or BambooHR-hosted URL for the PDF attachment
target_group: string // e.g. 'all_active', 'department:engineering', 'location:US'
cycle_deadline: date // ISO 8601, used to calculate reminder and escalation offsets
// Output
cycle_id: string // UUID generated for this distribution cycle, written to Sheet col A
employee_list: array // [{employee_id, full_name, email, department, manager_email}]
sheet_tab_id: string // Google Sheets tab name, format: 'cycle_{policy_id}_{YYYYMMDD}'
envelope_ids: array // [{employee_id, docusign_envelope_id, sent_at: ISO 8601}]
distribution_status: string // 'success' | 'partial' | 'failed', written to Sheet col H- BambooHR API tier must include custom fields access and employee directory read scope (OAuth 2.0 scope: employees:read, files:write). Confirm with BambooHR admin before build start.
- BambooHR employee list query must filter status = 'Active' and exclude employees where employment_status = 'On Leave (Extended)' to prevent sending to unreachable staff.
- DocuSign envelope template must be pre-approved and version-locked by a DocuSign admin (template_id stored as an environment variable DOCUSIGN_TEMPLATE_ID). The agent must not fall back to a draft or unlocked template under any circumstances.
- DocuSign bulk send uses the Envelopes: createEnvelope endpoint with composite templates; do not use the legacy BulkRecipient CSV method as it does not support webhook callbacks per envelope.
- Google Sheets tab naming convention must be enforced: 'cycle_{policy_id}_{YYYYMMDD}' to prevent collision across concurrent cycles. If a tab with that name already exists, the agent appends '_r{n}' and logs a warning.
- Dedupe: before creating envelopes, the agent checks the Sheet for any existing envelope_id for the same employee and policy_id. If found, it skips re-send and logs 'duplicate_skipped'.
- Confirm BambooHR webhook or cron fallback approach with process owner before build, as some BambooHR tiers do not emit webhooks on document field changes.
Runs on a daily schedule after the initial distribution. It polls DocuSign envelope status for each employee in the active cycle, identifies non-responders, sends a day-five Gmail reminder with a direct envelope link, and sends a day-ten Slack direct message to the employee and a separate Slack notification to their line manager. It keeps the Google Sheet status column current after every run. Estimated build: 8 hours. Complexity: Moderate.
// Input cycle_id: string // From active cycles index in Google Sheets employee_id: string // BambooHR employee ID envelope_id: string // DocuSign envelope ID from Policy Distribution Agent output sent_at: date // ISO 8601 timestamp of original send, used to calculate day offsets cycle_deadline: date // ISO 8601, used to suppress reminders after deadline has passed manager_email: string // From BambooHR employee record, used for Slack escalation lookup manager_slack_id: string // Slack user ID resolved from manager_email via users.lookupByEmail // Output reminder_sent_at: date // Timestamp written to Sheet col D when day-5 Gmail fires escalation_sent_at: date // Timestamp written to Sheet col E when day-10 Slack fires current_envelope_status: string // 'sent' | 'delivered' | 'completed' | 'declined' | 'voided' sheet_row_updated: boolean // True if batchUpdate call succeeded for this employee row // On escalation slack_message_employee: string // DM to employee Slack ID: direct link to DocuSign envelope slack_message_manager: string // DM to manager Slack ID: employee name, policy name, days overdue
- DocuSign status poll must use GET /v2.1/accounts/{accountId}/envelopes?envelope_ids={id} to batch multiple envelope checks per API call. Do not issue one HTTP request per employee; DocuSign rate limit is 1,000 requests per hour per account.
- Day offset is calculated from sent_at, not from the cron run date, to handle cycles that start mid-week or during a public holiday window.
- Gmail reminder must use a stored HTML template (environment variable GMAIL_REMINDER_TEMPLATE_ID or an inline template string). The DocuSign envelope signing URL must be fetched fresh per send using GET /envelopes/{id}/recipients to avoid expired URLs.
- Slack manager notification requires the manager_slack_id to be resolved via Slack users.lookupByEmail before message send. If the lookup fails (manager not in Slack workspace), fall back to a Gmail email to manager_email and log 'slack_fallback_email'.
- BambooHR reporting-line data must be confirmed as clean before go-live. If manager fields are blank or inconsistent, escalation routing will fail. A one-time data cleanse of supervisor_id in BambooHR is flagged as a prerequisite.
- Reminders must not fire after cycle_deadline has passed. Add a guard condition: if current_date > cycle_deadline, skip reminder and log 'deadline_passed_no_action'.
- Employees with envelope status 'declined' or 'voided' must be flagged in the Sheet with tone 'amber' and excluded from standard reminders. These cases route immediately to HR Manager review via a separate Slack alert.
Listens for DocuSign envelope completion webhooks and, on each event, retrieves the signed PDF, attaches it to the correct employee record in BambooHR, and updates the Google Sheet row to 'signed' with a timestamp. When all employees in a cycle have a completed status, the agent generates a completion report summarising the cycle and distributes it to the HR Manager. Estimated build: 6 hours. Complexity: Moderate.
// Input (per webhook event)
envelope_id: string // From DocuSign Connect payload
envelope_status: string // Must equal 'completed' to proceed; all others are no-ops
completed_at: date // ISO 8601 from DocuSign event payload
signer_email: string // Used to look up employee_id in the Sheet index
document_id: string // DocuSign document ID within the envelope for PDF download
// Output (per employee completion)
bamboohr_file_id: string // ID returned by BambooHR after successful document upload
bamboohr_category: string // Must be set to 'Signed Policies' (exact BambooHR category name)
sheet_status: string // 'signed' written to col C, completed_at timestamp to col F
cycle_complete: boolean // True when all employee rows for cycle_id show status = 'signed'
// On cycle completion
completion_report_url: string // Google Sheets export link (PDF or Sheets share), emailed to HR Manager
report_summary: object // {cycle_id, policy_name, total_employees, signed_count, pending_count, declined_count, completion_pct}- BambooHR document upload endpoint is POST /v1/employees/{id}/files/ with multipart/form-data. The category field must match the exact string 'Signed Policies' as configured in the BambooHR document categories admin panel. Confirm this category exists before build.
- DocuSign signed PDF is retrieved using GET /v2.1/accounts/{accountId}/envelopes/{envelopeId}/documents/{documentId} with Accept: application/pdf. The document must be streamed directly to BambooHR upload, not written to disk, to avoid PII data at rest on the orchestration server.
- Webhook reliability: DocuSign Connect may deliver events out of order or with delay. Implement idempotency using envelope_id as the deduplication key. If a completed event is received for an envelope already marked 'signed' in the Sheet, log 'idempotent_skip' and return 200.
- Completion report generation should use the Google Sheets API to read all rows for the cycle_id and produce a summary object. The report is emailed to the HR Manager via Gmail using a stored template (GMAIL_REPORT_TEMPLATE_ID).
- If a webhook event is not received within 2 hours of the expected signing window closing, a fallback polling job should run a final DocuSign status sweep and process any missed completions.
- BambooHR API write access (files:write scope) must be confirmed as part of tool access check milestone (Apr 14). Build cannot be completed without this permission.
03End-to-end data flow
// ============================================================
// POLICY ACKNOWLEDGEMENT TRACKING: END-TO-END DATA FLOW
// ============================================================
// --- TRIGGER ---
// BambooHR webhook fires on policy publish, or daily cron detects review_date = today
TRIGGER: BambooHR -> orchestration_layer
payload: {
policy_id: 'POL-2024-042',
policy_name: 'Remote Work Policy',
policy_version: 'v2.3',
policy_document_url: 'https://api.bamboohr.com/api/gateway.php/[company]/v1/files/...',
target_group: 'all_active',
cycle_deadline: '2024-06-15T17:00:00Z'
}
// --- AGENT 1: POLICY DISTRIBUTION AGENT ---
// Step 1: Fetch active employee list from BambooHR
BambooHR.GET /v1/employees/directory
filter: status='Active', employment_status!='On Leave (Extended)'
fields: id, firstName, lastName, workEmail, department, supervisorEmail
output -> employee_list: [
{ employee_id: 'E001', full_name: 'Jane Smith', email: 'jane@example.com',
department: 'Engineering', manager_email: 'mgr@example.com' },
// ... n employees
]
// Step 2: Seed Google Sheets tracking tab
GoogleSheets.batchUpdate -> tab: 'cycle_POL-2024-042_20240501'
columns: [cycle_id, employee_id, full_name, email, department, manager_email,
envelope_id, status, sent_at, reminder_at, escalation_at,
signed_at, bamboohr_file_id, notes]
initial_status: 'pending' (all rows)
output -> sheet_tab_id: 'cycle_POL-2024-042_20240501'
// Step 3: Create and send DocuSign envelopes (one per employee)
DocuSign.POST /v2.1/accounts/{accountId}/envelopes
template_id: ENV['DOCUSIGN_TEMPLATE_ID'] // pre-approved, version-locked
per employee: {
signers[0].email: employee.email,
signers[0].name: employee.full_name,
signers[0].clientUserId: employee.employee_id, // for embedded signing fallback
emailSubject: 'Action required: Please acknowledge Remote Work Policy v2.3',
templateRoles[0].tabs.textTabs: [{ tabLabel: 'PolicyVersion', value: 'v2.3' }]
}
output -> envelope_ids: [{ employee_id: 'E001', envelope_id: 'ENV-abc123', sent_at: ISO8601 }]
// Write envelope_ids back to Sheet col G (envelope_id) + col I (sent_at)
GoogleSheets.batchUpdate -> update rows with envelope_id and status='sent'
// ======================= AGENT 1 -> AGENT 2 HANDOFF =======================
// cycle_id, sheet_tab_id, employee_list, envelope_ids written to shared state (Sheet index tab)
// Agent 2 reads these fields on each daily cron run
// =========================================================================
// --- AGENT 2: REMINDER AND ESCALATION AGENT (daily cron 09:00) ---
// Step 4: Poll DocuSign envelope status for all non-completed envelopes
DocuSign.GET /v2.1/accounts/{accountId}/envelopes
envelope_ids: [batch of active envelope_ids from Sheet]
fields: envelopeId, status, completedDateTime, declinedDateTime
output -> status_map: { 'ENV-abc123': 'sent', 'ENV-def456': 'completed', ... }
// Step 5: Update Sheet col C (current_envelope_status) for all rows
GoogleSheets.batchUpdate -> col C = status_map[envelope_id] per row
// Step 6: Day-5 reminder (if days_since_sent >= 5 and status != 'completed')
// Fetch fresh signing URL to avoid expiry
DocuSign.GET /v2.1/accounts/{accountId}/envelopes/{envelopeId}/recipients
output -> signing_url: 'https://app.docusign.net/signing/...'
Gmail.messages.send
to: employee.email
template: ENV['GMAIL_REMINDER_TEMPLATE_ID']
vars: { employee_name, policy_name, policy_version, signing_url, deadline }
output -> reminder_sent_at -> Sheet col D
// Step 7: Day-10 escalation (if days_since_sent >= 10 and status != 'completed')
// Resolve manager Slack ID
Slack.users.lookupByEmail -> email: manager_email
on_success -> manager_slack_id
on_failure -> fallback: Gmail to manager_email, log 'slack_fallback_email'
// DM employee
Slack.chat.postMessage
channel: employee_slack_id // resolved same way as manager
text: 'Reminder: Your signature on {policy_name} is overdue. Please sign here: {signing_url}'
// DM manager
Slack.chat.postMessage
channel: manager_slack_id
text: '{employee_name} has not signed {policy_name} v{version}. {days} days overdue. Deadline: {deadline}.'
output -> escalation_sent_at -> Sheet col E
// ======================= AGENT 2 -> AGENT 3 HANDOFF =======================
// No direct call: Agent 3 is triggered independently by DocuSign webhook events
// Shared state continues via Google Sheets (cycle_id + employee_id as compound key)
// =========================================================================
// --- AGENT 3: COMPLETION AND ARCHIVING AGENT ---
// Step 8: Receive DocuSign Connect webhook (envelope status = 'completed')
WEBHOOK_RECEIVE: DocuSign -> orchestration_layer POST /webhooks/docusign-complete
payload: { envelopeId, status, completedDateTime, signers[0].email }
guard: if status != 'completed' -> return 200 (no-op)
dedup: if Sheet row already shows status='signed' -> log 'idempotent_skip', return 200
// Step 9: Download signed PDF
DocuSign.GET /v2.1/accounts/{accountId}/envelopes/{envelopeId}/documents/{documentId}
Accept: application/pdf
output -> pdf_stream (in-memory, not written to disk)
// Step 10: Upload to BambooHR employee record
BambooHR.POST /v1/employees/{employee_id}/files/
Content-Type: multipart/form-data
fields: { category: 'Signed Policies', fileName: '{policy_name}_v{version}_{employee_id}.pdf',
share: false }
body: pdf_stream
output -> bamboohr_file_id -> Sheet col M
// Step 11: Update Sheet row
GoogleSheets.batchUpdate
col C (status): 'signed'
col F (signed_at): completedDateTime (ISO 8601)
col M (bamboohr_file_id): bamboohr_file_id
// Step 12: Check cycle completion
GoogleSheets.get -> all rows for cycle_id where status != 'signed'
if pending_count == 0:
cycle_complete = true
report_summary = { cycle_id, policy_name, total_employees, signed_count,
pending_count: 0, declined_count, completion_pct: '100%' }
Gmail.messages.send -> HR Manager email
subject: 'Cycle complete: {policy_name} v{version} acknowledgement report'
attachment: Google Sheets export PDF URL
else:
cycle_complete = false // Agent 2 continues daily monitoring
// ============================================================
// END OF FLOW
// ============================================================04Recommended build stack
More documents for this process
Every document generated for Policy Acknowledgement Tracking.