Back to Performance Review Cycle

Integration and API Spec

The reference during the build: every tool connection, auth scope, field mapping, and error-handling rule.

4 pagesPDF · Technical
FS-DOC-05Technical

Integration and API Spec

Performance Review Cycle

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

This document is the definitive technical reference for every service-to-service connection in the Performance Review Cycle automation. It covers exact authentication methods, required OAuth scopes, webhook and polling configurations, field-level mappings between tools, credential store contents, and the full error handling and retry logic that governs each integration point. The FullSpec team builds and validates every connection described here. Refer to this document when configuring the credential store, verifying scope grants, or diagnosing integration failures.

01Tool inventory

Tool
Role in automation
Auth method
Min plan required
Used by agents
BambooHR
Employee data source; final ratings write-back
OAuth 2.0 (API key fallback available)
Essentials or higher (API access required)
Agent 1, Agent 3 (write-back)
Google Forms
Self-assessment and manager assessment capture
OAuth 2.0 via Google Workspace service account
Google Workspace Starter or higher
Agent 2, Agent 3
Google Sheets
Live submission status tracker; updated by Agent 2
OAuth 2.0 via Google Workspace service account
Google Workspace Starter or higher
Agent 2
Google Docs
Auto-generated manager summary documents
OAuth 2.0 via Google Workspace service account
Google Workspace Starter or higher
Agent 3
Gmail
Kick-off emails, chase reminders, summary notifications
OAuth 2.0 via Google Workspace service account
Google Workspace Starter or higher
Agent 1, Agent 2, Agent 3
Slack
Kick-off channel post, overdue nudges, deadline alerts
OAuth 2.0 (Bot Token, Slack app install)
Slack Pro or higher (for workflow triggers)
Agent 1, Agent 2
Orchestration layer
Hosts all three agent workflows; manages scheduling, credential store, and retry logic
Internal; credentials injected at runtime via secret store
N/A
All agents
Before you connect anything: provision sandbox or test credentials for every tool listed above and validate each connection end-to-end in a non-production environment before supplying production credentials to the orchestration layer. Never hardcode production API keys or tokens in workflow configuration files.

02Per-tool integration detail

BambooHR

Provides the authoritative employee list at cycle open and receives confirmed final ratings via API write-back after calibration. Consumed by Agent 1 (read) and the post-calibration write-back step (write).

Auth method
OAuth 2.0. Generate a dedicated API key under Settings > API Keys in BambooHR. Store as BAMBOOHR_API_KEY in the credential store. Base URL pattern: https://{subdomain}.bamboohr.com/api/gateway.php/{subdomain}/v1/
Required scopes
employees:read, customfields:read, tabular:write (for rating write-back). BambooHR API keys inherit the permissions of the generating user account; ensure that account holds the 'HR Manager' role or an equivalent custom role with the above field access.
Webhook / trigger setup
BambooHR does not natively emit a webhook on review cycle status change. The orchestration layer polls the custom field review_cycle_status on a 15-minute interval. When the field transitions to 'open', Agent 1 fires. Store the poll schedule in the workflow config; do not hardcode the interval.
Required configuration
Custom field IDs: review_cycle_status (text), review_cycle_deadline (date), review_rating_final (number). Employee report fields required: id, firstName, lastName, workEmail, supervisorId, supervisorEmail, department, status. All field IDs must be stored in the credential store (see Section 04), not hardcoded. Manager-to-employee mapping is derived from supervisorId at runtime.
Rate limits
BambooHR enforces a limit of approximately 1,000 API calls per hour per API key. At a volume of 80 to 150 employees per cycle with polling every 15 minutes, peak call volume is well under 200 calls per hour. Throttling is not required at current volume; implement exponential backoff on 429 responses as a safeguard.
Constraints
API access is gated by plan tier. Essentials and above support third-party API access; Core (legacy) plans may not. Confirm plan before build. The write-back step requires the orchestration layer to have write permission on the tabular data endpoint. Bulk write is not supported; ratings must be written one employee record at a time.
// Input (Agent 1 - read)
GET /employees/directory?fields=id,firstName,lastName,workEmail,supervisorId,supervisorEmail,department,status
GET /employees/{id}/customFields?field=review_cycle_status,review_cycle_deadline
// Output (post-calibration write-back)
POST /employees/{id}/tables/customTabularData
Body: { "review_rating_final": "<agreed_rating>", "review_notes": "<calibration_notes>" }
Google Forms

Hosts the employee self-assessment form and the manager assessment form. Agent 2 monitors for new responses via the Google Sheets linked response destination. Agent 3 reads matched response pairs to generate summaries.

Auth method
OAuth 2.0 via a Google Workspace service account. Grant the service account 'Editor' access on both Forms. Service account JSON key stored as GOOGLE_SERVICE_ACCOUNT_KEY in the credential store.
Required scopes
https://www.googleapis.com/auth/forms.responses.readonly, https://www.googleapis.com/auth/spreadsheets (for linked response sheet access), https://www.googleapis.com/auth/drive.readonly (to resolve form IDs from Drive)
Webhook / trigger setup
Google Forms does not support outbound webhooks natively. Link both forms to a Google Sheets response destination (Forms > Responses > Link to Sheets). Agent 2 polls the linked sheets on a 4-hour interval to detect new rows. The form IDs (SELF_ASSESSMENT_FORM_ID, MANAGER_ASSESSMENT_FORM_ID) and their linked sheet IDs (SELF_ASSESSMENT_SHEET_ID, MANAGER_ASSESSMENT_SHEET_ID) must be stored in the credential store.
Required configuration
Both forms must include a hidden or mandatory field employee_id (text) so that self-assessment and manager submissions can be matched on a common key. Manager form must additionally include a manager_id field. Form question ordering must be stable; the field mapping in Section 03 references column index positions and named headers. Do not reorder or rename form questions after go-live without updating the field mapping configuration.
Rate limits
Google Sheets API: 300 read requests per minute per project. With two sheets polled every 4 hours across a 150-person cohort, maximum read volume is under 10 requests per poll cycle. Throttling is not required at current volume.
Constraints
Response editing by submitters after the deadline must be disabled in form settings to prevent tracker state corruption. If peer review forms are added in a future cycle, each peer form must be assigned a unique form ID and linked sheet, and the matching logic in Agent 2 must be extended accordingly.
// Input (Agent 2 polls linked sheet)
GET https://sheets.googleapis.com/v4/spreadsheets/{SELF_ASSESSMENT_SHEET_ID}/values/Sheet1
GET https://sheets.googleapis.com/v4/spreadsheets/{MANAGER_ASSESSMENT_SHEET_ID}/values/Sheet1
// Output (rows consumed by Agent 2 and Agent 3)
Row structure: [timestamp, employee_id, q1_response, q2_response, ..., qN_response, submitter_email]
Google Sheets

Serves as the live submission status tracker maintained by Agent 2. Contains one row per employee with submission status columns for self-assessment and manager assessment, timestamps, and reminder log entries.

Auth method
OAuth 2.0 via the same Google Workspace service account used for Google Forms. No additional credential required. Service account must hold 'Editor' access on the tracker sheet.
Required scopes
https://www.googleapis.com/auth/spreadsheets (read and write), https://www.googleapis.com/auth/drive.file
Webhook / trigger setup
No inbound webhook required. Agent 2 writes to the tracker on each poll cycle. The tracker sheet ID must be stored as TRACKER_SHEET_ID in the credential store.
Required configuration
Tracker sheet column structure (fixed, do not alter after go-live): A: employee_id, B: employee_name, C: manager_id, D: manager_name, E: department, F: self_assessment_status (pending / submitted), G: self_assessment_timestamp, H: manager_assessment_status (pending / submitted), I: manager_assessment_timestamp, J: reminder_count, K: last_reminder_sent, L: summary_doc_url. Header row must be row 1. Data begins at row 2.
Rate limits
Google Sheets API write limit: 300 requests per minute per project. Agent 2 performs a batch update on each poll cycle (one batchUpdate call for up to 150 rows). This is well within limits. Use batchUpdate rather than individual cell writes to minimise call volume.
Constraints
The tracker sheet must not be edited manually during a live cycle without notifying the FullSpec team, as manual edits to status columns can corrupt the reminder logic. A separate 'admin notes' column (column M) is available for HR annotations that do not affect automation state.
// Input (Agent 2 writes on each poll)
PATCH https://sheets.googleapis.com/v4/spreadsheets/{TRACKER_SHEET_ID}/values/Sheet1!F{row}:L{row}
Body: { "values": [["submitted", "{timestamp}", "pending", "", reminder_count, last_reminder_sent, ""]] }
// Output (Agent 2 reads for overdue detection)
GET https://sheets.googleapis.com/v4/spreadsheets/{TRACKER_SHEET_ID}/values/Sheet1!A2:L
Integration and API SpecPage 1 of 3
FS-DOC-05Technical
Google Docs

Hosts the manager summary template and stores auto-generated per-employee summary documents in manager-specific Drive folders. Consumed exclusively by Agent 3.

Auth method
OAuth 2.0 via the Google Workspace service account. Service account must hold 'Editor' access on the summary template document and 'Editor' access on all manager Drive folders. SUMMARY_TEMPLATE_DOC_ID stored in the credential store.
Required scopes
https://www.googleapis.com/auth/documents, https://www.googleapis.com/auth/drive (to copy template and write to manager folders)
Webhook / trigger setup
No inbound webhook. Agent 3 is triggered internally when the submission matching logic confirms a complete pair (see Agent 3 trigger in Section 04).
Required configuration
The summary template document (SUMMARY_TEMPLATE_DOC_ID) must contain the following placeholder strings exactly as written, surrounded by double curly braces: {{employee_name}}, {{employee_id}}, {{department}}, {{manager_name}}, {{review_period}}, {{self_assessment_q1}} through {{self_assessment_qN}}, {{manager_assessment_q1}} through {{manager_assessment_qN}}, {{ai_summary_narrative}}, {{generated_date}}. Manager Drive folder IDs must be pre-created and stored as a JSON map keyed by manager_id in the credential store entry MANAGER_FOLDER_MAP.
Rate limits
Google Docs API: 300 requests per minute per project. At 150 employees per cycle, Agent 3 performs one document copy and one batchUpdate per employee, totalling approximately 300 API calls per full cycle. These calls are distributed over hours as pairs match, so instantaneous rate is low. Throttling is not required at current volume; add a 1-second delay between successive document generation calls as a conservative safeguard.
Constraints
Template placeholders are case-sensitive and must match exactly. Do not rename or reformat placeholders in the template after go-live. Document naming convention for generated files must be: 'Performance Review Summary - {employee_name} - {review_period}'. This ensures HR can locate documents via Drive search without accessing the tracker sheet.
// Agent 3: copy template, then replace placeholders
POST https://www.googleapis.com/drive/v3/files/{SUMMARY_TEMPLATE_DOC_ID}/copy
Body: { "name": "Performance Review Summary - {employee_name} - {review_period}", "parents": ["{manager_folder_id}"] }
POST https://docs.googleapis.com/v1/documents/{new_doc_id}:batchUpdate
Body: { "requests": [ { "replaceAllText": { "containsText": { "text": "{{employee_name}}" }, "replaceText": "{employee_name}" } }, ... ] }
Gmail

Dispatches kick-off emails (Agent 1), daily chase reminders and 48-hour deadline nudges (Agent 2), and manager summary-ready notifications (Agent 3). All mail is sent from a designated HR service mailbox, not an individual user account.

Auth method
OAuth 2.0 via the Google Workspace service account with domain-wide delegation enabled. The service account impersonates the designated HR mailbox (e.g. hr-reviews@[YourCompany.com]). Store the impersonation email as GMAIL_SENDER_ADDRESS in the credential store.
Required scopes
https://www.googleapis.com/auth/gmail.send, https://www.googleapis.com/auth/gmail.compose. Do NOT grant gmail.readonly or broader mail scopes; send-only is sufficient and reduces exposure.
Webhook / trigger setup
No inbound webhook required. All Gmail interactions are outbound sends initiated by the orchestration layer. Gmail Pub/Sub push notifications are not used in this automation.
Required configuration
Email template IDs or inline HTML bodies must be stored in the credential store or a version-controlled config file, not hardcoded in the workflow. Required template keys: KICKOFF_EMPLOYEE_TEMPLATE, KICKOFF_MANAGER_TEMPLATE, REMINDER_TEMPLATE, FINAL_NUDGE_TEMPLATE, SUMMARY_READY_TEMPLATE. Each template must contain the placeholders {{first_name}}, {{form_link}}, {{deadline_date}}, and {{summary_doc_url}} (where applicable). The self-assessment form link (SELF_ASSESSMENT_FORM_URL) and manager assessment form link (MANAGER_ASSESSMENT_FORM_URL) must be stored in the credential store.
Rate limits
Gmail API (service account with delegation): 250 quota units per second; 1 billion quota units per day. Sending to 150 recipients in a single Agent 1 run consumes approximately 150 units. Daily reminders to a maximum of 150 overdue submitters add a further 150 units per day. Well within daily quota. Implement a 200ms delay between individual send calls during bulk dispatch to avoid per-second quota bursts.
Constraints
Emails must be sent from the designated HR mailbox only. Individual HR team member mailboxes must not be used as the sender address, as delegation scope applies only to the designated mailbox. SPF and DKIM records for [YourCompany.com] must include Google Workspace to prevent kick-off emails landing in spam.
// Agent 1: kick-off send
POST https://gmail.googleapis.com/gmail/v1/users/{GMAIL_SENDER_ADDRESS}/messages/send
Body: { "raw": "<base64-encoded RFC 2822 message>" }
// Encoded message headers example
From: hr-reviews@[YourCompany.com]
To: {employee_email}
Subject: Your Performance Review is Now Open - Action Required by {deadline_date}
Content-Type: text/html; charset=UTF-8
Slack

Posts kick-off announcements to configured HR and team channels (Agent 1) and sends direct-message nudges to overdue submitters 48 hours before the deadline (Agent 2). Uses a dedicated Slack app with bot token authentication.

Auth method
OAuth 2.0 Bot Token. Install the dedicated 'HR Reviews Bot' Slack app to the workspace. Store the bot token as SLACK_BOT_TOKEN in the credential store. Do not use a user token or legacy webhook URL.
Required scopes
chat:write, chat:write.public (to post to channels the bot has not joined), im:write (for DM nudges), users:read (to resolve Slack user IDs from email addresses), users:read.email
Webhook / trigger setup
No inbound webhook required. All Slack interactions are outbound posts initiated by the orchestration layer via the Slack Web API. The bot must be added to each target channel before the first live cycle. Target channel IDs must be stored as SLACK_HR_CHANNEL_ID and SLACK_ANNOUNCEMENTS_CHANNEL_ID in the credential store.
Required configuration
Slack user ID resolution: Agent 1 calls users.lookupByEmail to convert employee work email addresses to Slack user IDs before sending DMs. Cache the email-to-user-ID map for the duration of each cycle run to avoid repeated lookups. Nudge DM template stored as SLACK_NUDGE_TEMPLATE in config. Block Kit JSON is preferred over plain text for all channel posts to support formatting.
Rate limits
Slack Web API tier 3 methods (chat.postMessage): 50 requests per minute. At 150 employees, a bulk DM nudge run requires 150 calls, which at 50/min takes approximately 3 minutes. The orchestration layer must respect this limit by batching DMs with a 1.2-second inter-call delay. Tier 1 methods (users.lookupByEmail): 20 requests per minute; cache results to avoid repeated calls.
Constraints
The Slack app must be installed by a Workspace Owner or Admin; a standard member cannot complete the OAuth install. If employees do not have a Slack account matching their work email, the DM nudge is skipped and the Gmail reminder is sent only. The orchestration layer must handle users.lookupByEmail 404 responses gracefully without halting the nudge run.
// Agent 1: channel announcement
POST https://slack.com/api/chat.postMessage
Headers: { Authorization: Bearer {SLACK_BOT_TOKEN} }
Body: { "channel": "{SLACK_ANNOUNCEMENTS_CHANNEL_ID}", "blocks": [{...Block Kit JSON...}] }
// Agent 2: direct message nudge
POST https://slack.com/api/chat.postMessage
Body: { "channel": "{slack_user_id}", "text": "Reminder: your performance review form is due in 48 hours. {form_link}" }

03Field mappings between tools

The tables below define the exact field mappings for each agent handoff in the workflow. Field names in monospace reflect the actual column headers or API property names that must be used in the integration configuration. Do not rename source fields without updating the corresponding destination mapping.

Agent 1 handoff: BambooHR to Gmail and Slack (cycle launch)

Source tool
Source field
Destination tool
Destination field
BambooHR
`id`
Gmail / Slack
`employee_id` (used for tracker keying)
BambooHR
`firstName`
Gmail
`{{first_name}}` in KICKOFF_EMPLOYEE_TEMPLATE
BambooHR
`lastName`
Gmail
`{{last_name}}` in KICKOFF_EMPLOYEE_TEMPLATE
BambooHR
`workEmail`
Gmail
`To:` header
BambooHR
`workEmail`
Slack
Input to `users.lookupByEmail` to resolve `slack_user_id`
BambooHR
`supervisorEmail`
Gmail
`To:` header for KICKOFF_MANAGER_TEMPLATE
BambooHR
`supervisorId`
Tracker Sheet
`manager_id` column C
BambooHR
`review_cycle_deadline`
Gmail
`{{deadline_date}}` in all email templates
BambooHR
`review_cycle_deadline`
Slack
`{{deadline_date}}` in Block Kit announcement
Config store
`SELF_ASSESSMENT_FORM_URL`
Gmail
`{{form_link}}` in KICKOFF_EMPLOYEE_TEMPLATE
Config store
`MANAGER_ASSESSMENT_FORM_URL`
Gmail
`{{form_link}}` in KICKOFF_MANAGER_TEMPLATE

Agent 2 handoff: Google Forms linked sheets to Google Sheets tracker and Gmail / Slack (submission tracking and reminders)

Source tool
Source field
Destination tool
Destination field
Self-assessment sheet
`employee_id`
Tracker Sheet
`employee_id` column A (match key)
Self-assessment sheet
`Timestamp`
Tracker Sheet
`self_assessment_timestamp` column G
Self-assessment sheet
`Timestamp` (not null)
Tracker Sheet
`self_assessment_status` column F -> `submitted`
Manager assessment sheet
`employee_id`
Tracker Sheet
`employee_id` column A (match key)
Manager assessment sheet
`Timestamp`
Tracker Sheet
`manager_assessment_timestamp` column I
Manager assessment sheet
`Timestamp` (not null)
Tracker Sheet
`manager_assessment_status` column H -> `submitted`
Tracker Sheet
`employee_id` (status=pending, deadline passed)
Gmail
`To:` header for REMINDER_TEMPLATE
Tracker Sheet
`reminder_count`
Tracker Sheet
`reminder_count` column J (increment by 1 per send)
Tracker Sheet
`last_reminder_sent`
Tracker Sheet
`last_reminder_sent` column K (ISO 8601 timestamp)
Tracker Sheet
`workEmail` (status=pending, T-48h)
Slack
Input to `users.lookupByEmail` for DM nudge

Agent 3 handoff: Google Forms linked sheets to Google Docs and Gmail (summary generation and notification)

Source tool
Source field
Destination tool
Destination field
Self-assessment sheet
`employee_id`
Google Docs
Match key; populates `{{employee_id}}` placeholder
Self-assessment sheet
`firstName` + `lastName`
Google Docs
`{{employee_name}}` placeholder
Self-assessment sheet
`department`
Google Docs
`{{department}}` placeholder
Self-assessment sheet
`q1_response` ... `qN_response`
Google Docs
`{{self_assessment_q1}}` ... `{{self_assessment_qN}}`
Manager assessment sheet
`manager_name`
Google Docs
`{{manager_name}}` placeholder
Manager assessment sheet
`q1_response` ... `qN_response`
Google Docs
`{{manager_assessment_q1}}` ... `{{manager_assessment_qN}}`
AI drafting step output
`narrative_text`
Google Docs
`{{ai_summary_narrative}}` placeholder
Orchestration layer
`review_period`
Google Docs
`{{review_period}}` placeholder (from config store `REVIEW_PERIOD_LABEL`)
Orchestration layer
Generated doc URL
Tracker Sheet
`summary_doc_url` column L
Google Docs
Generated doc URL
Gmail
`{{summary_doc_url}}` in SUMMARY_READY_TEMPLATE
BambooHR
`supervisorEmail`
Gmail
`To:` header for SUMMARY_READY_TEMPLATE
Integration and API SpecPage 2 of 3
FS-DOC-05Technical

04Build stack and orchestration

Orchestration layout
Three discrete workflows, one per agent. Each workflow is independently deployable and restartable. Agents do not share in-memory state; all shared state is held in the Google Sheets tracker (TRACKER_SHEET_ID). A single shared credential store services all three workflows at runtime; credentials are injected as environment variables or secret references, never embedded in workflow definitions.
Agent 1 trigger mechanism
Poll. The orchestration layer queries BambooHR every 15 minutes for the value of the review_cycle_status custom field. On transition from any value to 'open', Agent 1 fires once and sets an internal run-lock flag (stored as a boolean in the tracker sheet, column N: cycle_launch_sent) to prevent duplicate kick-off dispatch on subsequent polls.
Agent 2 trigger mechanism
Poll. Agent 2 runs on a 4-hour cron schedule throughout the active review window. On each run it reads all rows from the self-assessment linked sheet and the manager assessment linked sheet, reconciles against the tracker, updates statuses, and evaluates overdue logic. The daily reminder dispatch sub-step runs on a separate daily cron (09:00 local time) and the 48-hour Slack nudge sub-step is evaluated on each 4-hour poll against the deadline timestamp.
Agent 3 trigger mechanism
Event-driven internal trigger. At the end of each Agent 2 poll cycle, Agent 2 writes a list of newly matched employee IDs (both self-assessment and manager assessment now showing 'submitted') to a staging column (column O: pending_summary_generation) in the tracker sheet. A separate lightweight Agent 3 workflow polls column O every 30 minutes and processes each queued employee ID, then clears the cell. This decouples summary generation from the tracker poll cycle and prevents timeout issues on large cohorts.
Webhook signature validation
No inbound webhooks are used in this automation. All triggers are outbound polls from the orchestration layer. Signature validation is therefore not applicable. If a future iteration introduces a BambooHR webhook or Google Forms Apps Script push notification, HMAC-SHA256 signature validation must be implemented before processing the payload.
Credential store
All credentials and configuration constants are stored in the orchestration platform's built-in secret store or an equivalent environment variable layer. See code block below for full contents.
All values marked <...> must be populated before workflow activation. No credential or ID value is ever hardcoded in workflow definition files.
# Credential store contents
# BambooHR
BAMBOOHR_API_KEY=<api_key_from_bamboohr_settings>
BAMBOOHR_SUBDOMAIN=<your_bamboohr_subdomain>
BAMBOOHR_FIELD_REVIEW_STATUS=<custom_field_id_for_review_cycle_status>
BAMBOOHR_FIELD_REVIEW_DEADLINE=<custom_field_id_for_review_cycle_deadline>
BAMBOOHR_FIELD_FINAL_RATING=<custom_field_id_for_review_rating_final>

# Google Workspace (shared service account)
GOOGLE_SERVICE_ACCOUNT_KEY=<path_or_json_of_service_account_key_file>
GOOGLE_IMPERSONATION_EMAIL=hr-reviews@[YourCompany.com]

# Google Forms
SELF_ASSESSMENT_FORM_ID=<google_form_id_for_self_assessment>
SELF_ASSESSMENT_FORM_URL=<public_url_for_self_assessment_form>
SELF_ASSESSMENT_SHEET_ID=<google_sheets_id_of_linked_response_sheet>
MANAGER_ASSESSMENT_FORM_ID=<google_form_id_for_manager_assessment>
MANAGER_ASSESSMENT_FORM_URL=<public_url_for_manager_assessment_form>
MANAGER_ASSESSMENT_SHEET_ID=<google_sheets_id_of_linked_manager_response_sheet>

# Google Sheets tracker
TRACKER_SHEET_ID=<google_sheets_id_of_submission_tracker>

# Google Docs
SUMMARY_TEMPLATE_DOC_ID=<google_docs_id_of_summary_template>
MANAGER_FOLDER_MAP={"mgr_id_001":"drive_folder_id_001","mgr_id_002":"drive_folder_id_002"}

# Gmail
GMAIL_SENDER_ADDRESS=hr-reviews@[YourCompany.com]
KICKOFF_EMPLOYEE_TEMPLATE=<inline_html_or_template_id>
KICKOFF_MANAGER_TEMPLATE=<inline_html_or_template_id>
REMINDER_TEMPLATE=<inline_html_or_template_id>
FINAL_NUDGE_TEMPLATE=<inline_html_or_template_id>
SUMMARY_READY_TEMPLATE=<inline_html_or_template_id>

# Slack
SLACK_BOT_TOKEN=xoxb-<bot_token_from_slack_app_settings>
SLACK_HR_CHANNEL_ID=<channel_id_e.g._C012AB3CD>
SLACK_ANNOUNCEMENTS_CHANNEL_ID=<channel_id_e.g._C045EF6GH>

# Cycle config
REVIEW_PERIOD_LABEL=<e.g._H1_2025>
REMINDER_THRESHOLD_DAYS=1
FINAL_NUDGE_HOURS_BEFORE_DEADLINE=48

05Error handling and retry logic

Every integration point in the automation has a defined failure behaviour. Unhandled exceptions must never fail silently: any error that cannot be resolved by the retry logic must produce a visible alert to the HR team via the designated error notification channel (SLACK_HR_CHANNEL_ID) and write a structured error entry to the tracker sheet's error log tab.

Integration
Scenario
Required behaviour
BambooHR - poll for cycle status
API returns 401 Unauthorized
Halt poll cycle immediately. Post alert to SLACK_HR_CHANNEL_ID: 'BambooHR API key invalid or expired - Agent 1 paused.' Do not retry with the same key. Await manual credential rotation. Resume on next scheduled poll once credential is updated.
BambooHR - employee list fetch
API returns 429 Too Many Requests
Exponential backoff: wait 60s, retry. Wait 120s, retry. Wait 240s, retry. After 3 failed retries, post alert to SLACK_HR_CHANNEL_ID and skip current Agent 1 run. Agent 1 will re-attempt on the next 15-minute poll.
BambooHR - final ratings write-back
POST to tabular endpoint returns 400 Bad Request
Log the failing employee_id and response body to the error log tab in the tracker sheet. Continue processing remaining employees. After the full run, post a summary alert: 'Rating write-back failed for N employee(s). Manual review required.' Do not silently skip.
Gmail - kick-off or reminder send
API returns 500 Internal Server Error
Retry up to 3 times with 30-second intervals. If all retries fail, log the recipient email and template key to the error log tab. Post alert to SLACK_HR_CHANNEL_ID: 'Gmail send failed for {N} recipients. Manual follow-up required.' Do not silently skip any recipient.
Gmail - kick-off or reminder send
Recipient email address bounces (delivery failure report received)
Gmail send API call returns success (bounce is async). The orchestration layer does not natively receive bounce notifications. HR must monitor the GMAIL_SENDER_ADDRESS inbox for bounce notifications during the cycle. Bounced addresses must be corrected in BambooHR and the send retried manually.
Slack - channel post or DM nudge
users.lookupByEmail returns 404 (no matching Slack user)
Skip the Slack DM for that employee. The Gmail reminder is sent regardless. Log the employee_id and email in the error log tab. Do not halt the nudge run. Post a weekly digest to SLACK_HR_CHANNEL_ID listing employees with no Slack account match.
Slack - channel post or DM nudge
chat.postMessage returns rate_limited
Respect the retry_after value returned in the response header. Pause the send loop for the specified duration, then continue from the last successful send. Do not restart the entire nudge run from the beginning.
Google Forms - poll of linked response sheet
Sheet returns empty or unexpected column structure
Abort the current poll cycle for that sheet. Post alert to SLACK_HR_CHANNEL_ID: 'Response sheet structure mismatch detected - tracker update skipped for this cycle.' Log the sheet ID and row count returned. Do not write partial data to the tracker. Await manual investigation before resuming.
Google Sheets tracker - batchUpdate write
API returns 403 Forbidden
Post alert to SLACK_HR_CHANNEL_ID immediately: 'Tracker sheet write permission denied - Agent 2 halted.' Do not retry without resolving the permission issue. The service account must be re-granted Editor access on the tracker sheet. Log the timestamp of the failed write.
Google Docs - template copy or placeholder replace
Template document not found (404) or placeholder not matched
Skip summary generation for the affected employee. Write error details (employee_id, missing placeholder keys, response code) to the error log tab. Post alert to SLACK_HR_CHANNEL_ID: 'Summary generation failed for {employee_name}. Template check required.' HR must generate the summary manually for that employee. Do not send the SUMMARY_READY_TEMPLATE email until the document is confirmed complete.
AI drafting step - narrative generation
AI service returns timeout or empty response
Retry once after 15 seconds. If the second attempt also fails, generate the summary document with all form response fields populated but with the {{ai_summary_narrative}} placeholder replaced by the literal string '[AI draft unavailable - please complete this section manually]'. Continue with document delivery. Log the failure and notify the manager in the SUMMARY_READY_TEMPLATE email that the narrative section requires manual completion.
All integrations
Unhandled exception (unexpected error type not covered above)
Catch all unhandled exceptions at the workflow level. Write the full error payload (timestamp, workflow name, step name, error message, stack trace if available) to the error log tab in the tracker sheet. Post an immediate alert to SLACK_HR_CHANNEL_ID: 'Unhandled exception in {workflow_name} at step {step_name}. Manual intervention required.' Contact support@gofullspec.com with the error log entry if the issue cannot be resolved within 30 minutes.
All error log entries must include: timestamp (ISO 8601), workflow name, step name, affected record identifier (employee_id where applicable), HTTP status code, and the full error message. The error log tab in the tracker sheet is the single source of truth for integration failures during a live cycle. Review this tab at the start of each HR working day during the review window.
Integration and API SpecPage 3 of 3

More documents for this process

Every document generated for Performance Review Cycle.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Developer Handover Pack
Technical · Developer
View
Test and QA Plan
Quality · Developer
View