Back to Employee Satisfaction & Pulse Surveys

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

Employee Satisfaction and Pulse Surveys

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

This document is the authoritative technical reference for every integration used in the Employee Satisfaction and Pulse Survey automation. It covers authentication methods, required OAuth scopes, webhook configuration, field mappings between tools, credential store contents, orchestration layout, and the full error-handling contract. The FullSpec team uses this specification during build and QA; it also serves as the ongoing maintenance reference for anyone making changes after go-live.

01Tool inventory

Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
BambooHR
Employee list source: query active employees, filter status
API key (HTTP header)
Any plan with API access enabled by admin
Agent 1: Survey Distribution Agent
Typeform
Survey hosting, response collection, and response status polling
OAuth 2.0 / personal access token
Business plan (webhook + API response export)
Agent 1, Agent 2
Gmail
Send personalised survey invitations and single reminder emails
OAuth 2.0 (Google identity)
Any Google Workspace account
Agent 1: Survey Distribution Agent
Google Sheets
Response log, invited-list tracking, scoring formulas, trend data
OAuth 2.0 (Google identity, service account recommended)
Any Google Workspace account
Agent 1, Agent 2
Notion
AI narrative report drafting, HR review page, report storage
OAuth 2.0 (Notion integration token)
Any paid Notion plan with API access
Agent 2: Response Analysis Agent
Slack
Post response-rate alerts to HR channel and report link to leadership channel
OAuth 2.0 (Bot token via Slack app)
Any Slack plan (free plan supports incoming webhooks)
Agent 2: Response Analysis Agent
Orchestration layer (automation platform)
Workflow scheduling, step sequencing, credential management, retry logic
Internal credential store, platform API keys
Platform subscription appropriate to workflow count and execution volume
Both agents
Before you connect anything: configure and validate every integration against a sandbox or test account before touching production credentials. For BambooHR, use a read-only API key scoped to a test subdomain. For Typeform, use a test form on the Business plan. For Gmail and Google Sheets, use a dedicated automation service account. Only promote credentials to production after all connections pass the integration tests in the Test and QA Plan.
Integration and API SpecPage 1 of 4
FS-DOC-05Technical

02Per-tool integration detail

BambooHR

Provides the authoritative active employee list at the start of each survey cycle. The automation queries the Employees endpoint, filters by employment status, and returns a structured list of name, email, department, and hire date fields.

Auth method
API key passed as a custom HTTP header: Authorization: Basic base64(apikey:x). Key is stored in the credential store as BAMBOOHR_API_KEY. Never hardcoded.
Required scopes
Read access to the Employees resource. BambooHR does not use OAuth scopes in the traditional sense; the API key must be generated by an admin account and must have the Employee Directory permission enabled in BambooHR settings.
Webhook / trigger
No inbound webhook used. The automation polls the API at workflow trigger time on the configured schedule (weekly or monthly). No persistent subscription required.
Required configuration
BAMBOOHR_SUBDOMAIN stored in credential store (e.g. yourcompany). Employment status filter value stored as BAMBOOHR_ACTIVE_STATUS_FILTER (typically 'Active'). Contractor exclusion handled by filtering on the Employee Type field value stored as BAMBOOHR_EXCLUDE_TYPE (e.g. 'Contractor').
Rate limits
BambooHR enforces a limit of approximately 50 API requests per minute per API key. At current volume (40 to 150 employees, one list pull per cycle, 4 cycles per month), a single paginated request per cycle is sufficient. Throttling is not required at current volume. If employee count exceeds 1,000, paginate using the offset parameter and add a 1-second delay between page requests.
Constraints
Partially deactivated employees (e.g. on leave) must be handled by the status filter configuration. The API returns all fields by default; request only required fields using the fields query parameter to reduce payload size. BambooHR does not guarantee real-time sync; allow up to 15 minutes for newly activated accounts to appear.
// Endpoint
GET https://{BAMBOOHR_SUBDOMAIN}.bamboohr.com/api/gateway.php/{BAMBOOHR_SUBDOMAIN}/v1/employees/directory
// Query params
fields=id,firstName,lastName,workEmail,department,hireDate,employmentHistoryStatus
// Filter applied in-workflow
employmentHistoryStatus == BAMBOOHR_ACTIVE_STATUS_FILTER AND employeeType != BAMBOOHR_EXCLUDE_TYPE
// Output shape per employee
{ id, firstName, lastName, workEmail, department, hireDate }
Typeform

Hosts the survey form and provides response data via API after the survey window closes. During the survey window, the automation polls the Responses endpoint to identify non-responders for the reminder step.

Auth method
Personal access token passed as Authorization: Bearer {TYPEFORM_ACCESS_TOKEN}. Token stored in credential store as TYPEFORM_ACCESS_TOKEN. Alternatively, OAuth 2.0 app credentials (TYPEFORM_CLIENT_ID, TYPEFORM_CLIENT_SECRET) can be used for a longer-lived integration.
Required scopes
responses:read, forms:read, webhooks:write, webhooks:read. These scopes cover response polling, form metadata retrieval, and webhook registration for the survey-close event.
Webhook / trigger
Register a webhook on the target form using POST /forms/{TYPEFORM_FORM_ID}/webhooks. Set the event to form_response and point it at the orchestration layer's inbound webhook URL. Validate the signature header X-Typeform-Signature (HMAC-SHA256 using TYPEFORM_WEBHOOK_SECRET stored in credential store) before processing any payload. The webhook fires per individual response; for end-of-window aggregation the automation uses a scheduled poll rather than relying solely on the webhook stream.
Required configuration
TYPEFORM_FORM_ID stored in credential store (not hardcoded). TYPEFORM_WORKSPACE_ID for form listing. TYPEFORM_WEBHOOK_SECRET for signature validation. Survey questions must include a hidden field named employee_id populated at send time with the BambooHR employee ID so responses can be matched to the invited list without relying on email address alone.
Rate limits
Typeform API allows 4 requests per second and 200 requests per minute on the Business plan. At current volume (150 employees maximum, polled once mid-window and once at close), total API calls per cycle are under 10. Throttling is not required at current volume.
Constraints
Typeform API does not return partially completed responses. Any employee who started but did not submit the form appears as a non-responder during the reminder check. This is by design and documented in the implementation notes. The automation must not send more than one reminder per employee per cycle; track reminder-sent status in Google Sheets.
// Poll responses endpoint
GET https://api.typeform.com/forms/{TYPEFORM_FORM_ID}/responses
// Query params for incremental poll
?page_size=200&since={SURVEY_OPEN_TIMESTAMP}&fields=hidden,submitted_at,answers
// Inbound webhook payload (per response)
{ form_response: { hidden: { employee_id }, submitted_at, answers: [...] } }
// Signature validation
X-Typeform-Signature: sha256=HMAC(TYPEFORM_WEBHOOK_SECRET, raw_body)
Gmail

Sends personalised survey invitation emails and the single automated reminder to non-responders. Uses the Gmail API with OAuth 2.0 to send on behalf of the configured HR sending address.

Auth method
OAuth 2.0. Credentials stored as GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, GMAIL_REFRESH_TOKEN in credential store. The refresh token must be generated via the Google OAuth consent flow against the sending account and stored at setup. Access tokens are refreshed automatically by the orchestration layer.
Required scopes
https://www.googleapis.com/auth/gmail.send. This is the minimum scope required. Do not request broader scopes such as gmail.modify or gmail.readonly unless audit logging of sent mail is explicitly required.
Webhook / trigger
Gmail is used as an outbound action only. No inbound webhook or Pub/Sub subscription is required for this process.
Required configuration
GMAIL_SENDING_ADDRESS stored in credential store (the HR sender email). Email templates stored in the orchestration platform as named templates (INVITE_TEMPLATE_ID, REMINDER_TEMPLATE_ID) with placeholders {{first_name}}, {{survey_link}}, {{survey_close_date}}. Template IDs stored in credential store, not hardcoded. Reply-to address configured as a separate credential GMAIL_REPLY_TO if different from the sending address.
Rate limits
Gmail API enforces a sending limit of 2,000 messages per day for Google Workspace accounts and 500 per day for personal Gmail. At current volume (150 employees for invite plus up to 150 for reminder), maximum 300 sends per cycle, well within limits. Throttling is not required. If employee count exceeds 1,000, implement a 100ms delay between sends and monitor daily quota usage.
Constraints
All emails must be sent individually (one API call per recipient) to allow personalisation via hidden Typeform fields. Batch sending to a BCC list is not permitted as it prevents employee_id injection into the survey link. The reminder step must check the Google Sheets reminder_sent flag before sending to prevent duplicate reminders.
// Send invitation
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
// Message body (RFC 2822, base64url encoded)
To: {workEmail}
Subject: [YourCompany.com] Your voice matters - complete our pulse survey
Body: personalised from INVITE_TEMPLATE_ID with {{ first_name }}, {{ survey_link }}
// survey_link includes hidden field: ?employee_id={bamboohr_id}
// Reminder send follows identical structure using REMINDER_TEMPLATE_ID
Google Sheets

Serves as the central data store for the automation: logging invited employees, tracking response and reminder status during the survey window, and holding aggregated scores and trend data after the window closes.

Auth method
OAuth 2.0 via a Google service account. Credentials stored as GSHEETS_SERVICE_ACCOUNT_KEY (JSON key file content) in the credential store. The service account must be granted Editor access to the target spreadsheet. Do not use personal OAuth tokens for service-to-service calls.
Required scopes
https://www.googleapis.com/auth/spreadsheets (read and write to spreadsheets). https://www.googleapis.com/auth/drive.file is not required unless the automation needs to create new spreadsheet files programmatically.
Webhook / trigger
Google Sheets does not support outbound webhooks natively. The orchestration layer polls the sheet or writes to it as an action. No subscription setup required.
Required configuration
GSHEETS_SPREADSHEET_ID stored in credential store. Sheet tab structure must be pre-configured before build: tab 'Invited' (columns: bamboohr_id, first_name, last_name, work_email, department, invite_sent_at, response_received, reminder_sent), tab 'Responses' (columns: bamboohr_id, submitted_at, enps_score, q1_score through q10_score, department), tab 'Scores' (columns: cycle_date, avg_enps, avg_category_1 through avg_category_5, response_rate_pct, prior_cycle_enps, shift_pct). Scoring formulas pre-built in the Scores tab and referenced by column name, not by cell address.
Rate limits
Google Sheets API allows 300 requests per minute per project and 60 requests per minute per user. At current volume, the automation makes approximately 150 row-append calls per invite cycle and 200 row-update calls per reminder check. This is within limits. Throttling is not required at current volume. Batch append using the batchUpdate endpoint if employee count grows beyond 500.
Constraints
Row lookups to check reminder_sent and response_received status must use the bamboohr_id column as the key, not row index, to remain stable if rows are manually reordered. The automation must not delete rows from the Invited tab during an active cycle; only update in place.
// Append invited employee row
POST /v4/spreadsheets/{GSHEETS_SPREADSHEET_ID}/values/Invited!A:H:append
// Update response_received flag
PUT /v4/spreadsheets/{GSHEETS_SPREADSHEET_ID}/values/Invited!F{row}
// Append response data row
POST /v4/spreadsheets/{GSHEETS_SPREADSHEET_ID}/values/Responses!A:M:append
// Read Scores tab for trend comparison
GET /v4/spreadsheets/{GSHEETS_SPREADSHEET_ID}/values/Scores!A:L
Notion

Receives the AI-generated narrative report as a new page in a designated HR database. HR reviews the draft page in Notion before any distribution step fires. The page includes scored data, trend commentary, and flagged department scores below threshold.

Auth method
Notion integration token (internal integration). Token stored in credential store as NOTION_INTEGRATION_TOKEN. The integration must be manually connected to the target database page in the Notion UI by an admin before the automation can write to it.
Required scopes
Notion internal integrations do not use OAuth scopes in the traditional sense. The integration must have Insert content and Read content capabilities enabled in the Notion integration settings. Update content capability is required if the automation updates an existing page rather than creating a new one each cycle.
Webhook / trigger
Notion does not provide native outbound webhooks. The automation writes to Notion as an action and relies on the HR reviewer to manually trigger the distribution step (Slack post and Gmail send) after approving the report. The distribution step must not fire automatically without HR confirmation.
Required configuration
NOTION_DATABASE_ID stored in credential store (the HR survey reports database). NOTION_LOW_SCORE_THRESHOLD stored in credential store (default: 6 on a 10-point scale; adjust per client preference). Report page template structure: Title property (Survey Report: {cycle_date}), Status property (Draft / Approved), eNPS Score property (number), Response Rate property (number), Flagged Departments property (multi-select), and a body block with the AI narrative text. NOTION_REPORT_TEMPLATE_PAGE_ID stored in credential store if using page duplication rather than programmatic creation.
Rate limits
Notion API enforces a rate limit of 3 requests per second. A single report page creation involves approximately 5 to 10 API calls (create page, append heading blocks, append paragraph blocks, set properties). This is well within limits at current volume. Throttling is not required.
Constraints
The Notion API has a maximum block content size of 2,000 characters per text block. Long AI narratives must be split into multiple paragraph blocks. The integration cannot access pages it has not been explicitly connected to in the Notion UI; this connection step must be completed by the client admin during onboarding.
// Create report page in database
POST https://api.notion.com/v1/pages
// Headers
Authorization: Bearer {NOTION_INTEGRATION_TOKEN}
Notion-Version: 2022-06-28
// Body (abbreviated)
{ parent: { database_id: NOTION_DATABASE_ID },
  properties: { Title: 'Survey Report: {cycle_date}', Status: 'Draft',
    eNPS_Score: {avg_enps}, Response_Rate: {response_rate_pct},
    Flagged_Departments: [{dept_name}] },
  children: [ heading_block, ...paragraph_blocks (AI narrative, split at 2000 chars) ] }
Slack

Posts two types of messages: a mid-cycle response rate alert to the HR channel, and a post-approval report link to the leadership channel. Both use a Slack bot app with incoming webhook URLs or the Slack Web API chat.postMessage method.

Auth method
Slack Bot token (xoxb-...) stored in credential store as SLACK_BOT_TOKEN. Generated by creating a Slack app in the workspace and installing it. Alternatively, incoming webhook URLs can be used per channel; stored as SLACK_HR_WEBHOOK_URL and SLACK_LEADERSHIP_WEBHOOK_URL in the credential store.
Required scopes
chat:write (to post messages to channels the bot is a member of). channels:read (to resolve channel names to IDs if needed). If using incoming webhooks only, no OAuth scopes are required beyond the webhook URL itself.
Webhook / trigger
Slack is used as an outbound action only. No inbound Slack event subscription is required for this process.
Required configuration
SLACK_HR_CHANNEL_ID stored in credential store (the HR operations channel). SLACK_LEADERSHIP_CHANNEL_ID stored in credential store (the leadership report channel). Message templates defined in the orchestration platform: SLACK_RATE_ALERT_TEMPLATE (mid-cycle, includes response_rate_pct and survey_close_date), SLACK_REPORT_TEMPLATE (post-approval, includes notion_report_url and cycle_date). The bot must be manually added to both channels by a Slack admin before go-live.
Rate limits
Slack Web API enforces a Tier 3 rate limit of 50 requests per minute for chat.postMessage. The automation sends a maximum of 2 messages per survey cycle. Throttling is not required at any foreseeable volume.
Constraints
The leadership channel post must only fire after HR has set the Notion page Status property to 'Approved'. The automation must poll the Notion page status (or receive an explicit trigger) before posting to the leadership channel. Do not post the report link automatically on page creation.
// Mid-cycle HR alert
POST https://slack.com/api/chat.postMessage
{ channel: SLACK_HR_CHANNEL_ID,
  text: 'Pulse survey update: {response_rate_pct}% responded as of {now}. Survey closes {survey_close_date}.' }
// Post-approval leadership notification
{ channel: SLACK_LEADERSHIP_CHANNEL_ID,
  text: 'Survey report ready for {cycle_date}. Review here: {notion_report_url}' }
Integration and API SpecPage 2 of 4
FS-DOC-05Technical

03Field mappings between tools

The tables below define every field handoff between tools across both agents. Use exact field names as shown; any deviation will break lookup logic in Google Sheets and the Notion page builder.

Agent 1 handoff: BambooHR to Gmail and Google Sheets (invitation and tracking step)

Source tool
Source field
Destination tool
Destination field
BambooHR
`id`
Google Sheets
`bamboohr_id`
BambooHR
`firstName`
Google Sheets
`first_name`
BambooHR
`lastName`
Google Sheets
`last_name`
BambooHR
`workEmail`
Google Sheets
`work_email`
BambooHR
`department`
Google Sheets
`department`
BambooHR
`hireDate`
Google Sheets
`hire_date`
BambooHR
`workEmail`
Gmail
`To` (recipient address)
BambooHR
`firstName`
Gmail
`{{first_name}}` in INVITE_TEMPLATE_ID
BambooHR
`id`
Typeform
`employee_id` (hidden field appended to survey URL as query param)

Agent 1 handoff: Typeform to Google Sheets (response status update during survey window)

Source tool
Source field
Destination tool
Destination field
Typeform
`form_response.hidden.employee_id`
Google Sheets
`bamboohr_id` (lookup key)
Typeform
`form_response.submitted_at`
Google Sheets
`response_received_at`
Typeform
`form_response.submitted_at` (presence)
Google Sheets
`response_received` (boolean: TRUE)
Google Sheets
`response_received` == FALSE AND `reminder_sent` == FALSE
Gmail
Triggers reminder send to `work_email`
Gmail
reminder send confirmed
Google Sheets
`reminder_sent` = TRUE, `reminder_sent_at` = timestamp

Agent 2 handoff: Typeform to Google Sheets (response aggregation after survey close)

Source tool
Source field
Destination tool
Destination field
Typeform
`form_response.hidden.employee_id`
Google Sheets (Responses tab)
`bamboohr_id`
Typeform
`form_response.submitted_at`
Google Sheets (Responses tab)
`submitted_at`
Typeform
`form_response.answers[field_id=enps].number`
Google Sheets (Responses tab)
`enps_score`
Typeform
`form_response.answers[field_id=q1].number`
Google Sheets (Responses tab)
`q1_score`
Typeform
`form_response.answers[field_id=q2].number`
Google Sheets (Responses tab)
`q2_score`
Typeform
`form_response.answers[field_id=q3].number`
Google Sheets (Responses tab)
`q3_score`
Typeform
`form_response.answers[field_id=q4].number`
Google Sheets (Responses tab)
`q4_score`
Typeform
`form_response.answers[field_id=q5].number`
Google Sheets (Responses tab)
`q5_score`
Typeform
`form_response.answers[field_id=department_hidden].text`
Google Sheets (Responses tab)
`department`

Agent 2 handoff: Google Sheets (Scores tab) to Notion (report page creation)

Source tool
Source field
Destination tool
Destination field
Google Sheets (Scores tab)
`avg_enps`
Notion page property
`eNPS_Score`
Google Sheets (Scores tab)
`response_rate_pct`
Notion page property
`Response_Rate`
Google Sheets (Scores tab)
`cycle_date`
Notion page property
`Title` (formatted: 'Survey Report: {cycle_date}')
Google Sheets (Scores tab)
`shift_pct`
Notion body block
Included in AI narrative paragraph as trend commentary
Google Sheets (Scores tab)
`flagged_dept_names` (computed: avg score < NOTION_LOW_SCORE_THRESHOLD)
Notion page property
`Flagged_Departments` (multi-select)
Google Sheets (Scores tab)
`avg_category_1` through `avg_category_5`
Notion body block
Category score rows in AI narrative section
Notion page property
`Status` == 'Approved'
Slack (leadership channel)
Triggers `SLACK_REPORT_TEMPLATE` post with `notion_report_url`
Notion page property
`Status` == 'Approved'
Gmail
Triggers leadership distribution email with Notion report URL
Integration and API SpecPage 3 of 4
FS-DOC-05Technical

04Build stack and orchestration

Orchestration layout
Two discrete workflows: one per agent. Workflow 1 (Survey Distribution Agent) and Workflow 2 (Response Analysis Agent) share a single credential store but run independently. Neither workflow calls the other directly; Workflow 2 is triggered by the survey window close condition, not by a success event from Workflow 1.
Workflow 1 trigger mechanism
Scheduled poll trigger. Fires on a configured CRON expression (e.g. 0 8 * * 1 for weekly Monday 08:00, or 0 8 1 * * for monthly first of month). Trigger configuration stored as SURVEY_SCHEDULE_CRON in the credential store. A secondary conditional path fires when a new hire's 30-day mark is detected by comparing BambooHR hireDate against today's date at trigger time.
Workflow 2 trigger mechanism
Scheduled poll trigger that checks whether the SURVEY_CLOSE_DATETIME credential store value is in the past. Polls every 30 minutes during the survey window. Alternatively, if HR manually closes the survey in Typeform, the Typeform webhook (form_response events cease and the form is deactivated) can serve as an additional signal; the workflow must validate form deactivation status via GET /forms/{TYPEFORM_FORM_ID} before proceeding.
Webhook signature validation
All inbound Typeform webhook payloads must be validated using HMAC-SHA256 against TYPEFORM_WEBHOOK_SECRET before any workflow logic executes. Reject and log any request where the computed signature does not match X-Typeform-Signature. Return HTTP 200 to Typeform regardless of validation outcome to prevent retry storms, but do not process the payload on failure.
Credential store structure
All secrets and configuration values are stored in the orchestration platform's native encrypted credential store. No values are hardcoded in workflow logic. See code block below for the full credential inventory.
AI narrative generation
The Response Analysis Agent passes the aggregated Scores tab data to an LLM API call (model and endpoint configured via AI_MODEL_ENDPOINT and AI_API_KEY in the credential store). The prompt template is stored as a workflow constant (not in the credential store) and instructs the model to produce a plain-English summary, trend commentary, and flagged department notes. Output is split into blocks of no more than 2,000 characters before writing to Notion.
Environment separation
Maintain separate credential store entries for sandbox and production environments. Prefix sandbox credentials with SANDBOX_ (e.g. SANDBOX_BAMBOOHR_API_KEY). Workflow environment mode is toggled via the ENV flag in the credential store.
Credential store contents: all entries must be populated before first workflow execution
// ============================================================
// CREDENTIAL STORE CONTENTS
// All values encrypted at rest. Never log or expose in workflow output.
// ============================================================

// BambooHR
BAMBOOHR_API_KEY            = <api_key_from_bamboohr_admin>
BAMBOOHR_SUBDOMAIN          = <yourcompany>
BAMBOOHR_ACTIVE_STATUS_FILTER = 'Active'
BAMBOOHR_EXCLUDE_TYPE       = 'Contractor'

// Typeform
TYPEFORM_ACCESS_TOKEN       = <personal_access_token_or_oauth_token>
TYPEFORM_CLIENT_ID          = <oauth_app_client_id>
TYPEFORM_CLIENT_SECRET      = <oauth_app_client_secret>
TYPEFORM_FORM_ID            = <target_survey_form_id>
TYPEFORM_WORKSPACE_ID       = <workspace_id>
TYPEFORM_WEBHOOK_SECRET     = <hmac_signing_secret>

// Gmail
GMAIL_CLIENT_ID             = <google_oauth_client_id>
GMAIL_CLIENT_SECRET         = <google_oauth_client_secret>
GMAIL_REFRESH_TOKEN         = <oauth_refresh_token_for_sending_account>
GMAIL_SENDING_ADDRESS       = hr-surveys@yourcompany.com
GMAIL_REPLY_TO              = hr@yourcompany.com
INVITE_TEMPLATE_ID          = <orchestration_platform_template_id_invite>
REMINDER_TEMPLATE_ID        = <orchestration_platform_template_id_reminder>

// Google Sheets
GSHEETS_SERVICE_ACCOUNT_KEY = <json_key_file_content_base64_encoded>
GSHEETS_SPREADSHEET_ID      = <target_spreadsheet_id>

// Notion
NOTION_INTEGRATION_TOKEN    = <secret_integration_token>
NOTION_DATABASE_ID          = <hr_survey_reports_database_id>
NOTION_LOW_SCORE_THRESHOLD  = 6
NOTION_REPORT_TEMPLATE_PAGE_ID = <optional_template_page_id>

// Slack
SLACK_BOT_TOKEN             = <xoxb_bot_token>
SLACK_HR_CHANNEL_ID         = <hr_operations_channel_id>
SLACK_LEADERSHIP_CHANNEL_ID = <leadership_channel_id>
SLACK_HR_WEBHOOK_URL        = <optional_incoming_webhook_url_hr>
SLACK_LEADERSHIP_WEBHOOK_URL = <optional_incoming_webhook_url_leadership>

// AI / LLM
AI_MODEL_ENDPOINT           = <llm_api_endpoint>
AI_API_KEY                  = <llm_api_key>

// Schedule and configuration
SURVEY_SCHEDULE_CRON        = '0 8 1 * *'  // monthly; override for weekly pulse
SURVEY_WINDOW_DAYS          = 7
SURVEY_CLOSE_DATETIME       = <set_at_workflow_start_time_plus_SURVEY_WINDOW_DAYS>

// Environment
ENV                         = 'sandbox'  // switch to 'production' at go-live

05Error handling and retry logic

Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently. All errors must be caught, logged to the orchestration platform's execution log, and routed to the appropriate alert path. The table below covers all integration failure scenarios across both agents.

Integration
Scenario
Required behaviour
BambooHR API
API key invalid or expired (401 Unauthorized)
Halt workflow immediately. Log error with timestamp and HTTP status. Send alert to SLACK_HR_CHANNEL_ID: 'BambooHR authentication failed - survey cycle cannot start. Check BAMBOOHR_API_KEY.' No retry; credential rotation required before next execution.
BambooHR API
API rate limit hit (429 Too Many Requests)
Retry with exponential backoff: wait 60s, then 120s, then 240s. Maximum 3 retries. If all retries fail, halt workflow and alert HR channel. Log retry count and final HTTP status.
BambooHR API
Empty employee list returned (200 OK, 0 records)
Halt workflow. Do not proceed to Gmail send step. Log warning: 'BambooHR returned zero active employees. Verify BAMBOOHR_ACTIVE_STATUS_FILTER and BAMBOOHR_EXCLUDE_TYPE values.' Alert HR channel. Require manual confirmation before retry.
Gmail API
OAuth token expired or refresh fails (401 Unauthorized)
Attempt one token refresh using GMAIL_REFRESH_TOKEN. If refresh succeeds, continue. If refresh fails, halt workflow, log error, and alert HR channel: 'Gmail authentication failed - invitations not sent. Re-authorise OAuth credentials.' Do not retry send until credentials are refreshed.
Gmail API
Single send fails for one recipient (e.g. invalid address, 4xx response)
Log failed recipient (bamboohr_id, work_email, HTTP status). Skip that recipient and continue sending to remaining employees. After all sends complete, write failed recipients to a 'send_failed' column in the Google Sheets Invited tab. Alert HR channel with count of failed sends.
Gmail API
Daily send quota reached (429 or 403 rateLimitExceeded)
Pause sends. Log position in send queue (last successful bamboohr_id). Wait until the following day at 08:00 and resume from the logged position. Alert HR channel with expected resume time. Do not restart from the beginning of the list.
Typeform API
Inbound webhook signature validation fails
Return HTTP 200 to Typeform (prevents retry storm). Do not process payload. Log event: 'Typeform webhook signature mismatch - payload discarded.' If more than 3 consecutive validation failures occur, alert HR channel and pause webhook processing until investigated.
Typeform API
Response poll returns unexpected schema (missing hidden.employee_id field)
Log malformed response with Typeform response_id and submitted_at. Skip that response in the current cycle. Do not overwrite any existing Google Sheets data. After the window closes, surface a list of unmatched responses to HR for manual reconciliation.
Google Sheets API
Write fails (503 Service Unavailable or network timeout)
Retry with exponential backoff: wait 10s, 30s, 60s. Maximum 3 retries. If all retries fail, store the failed write payload in the orchestration platform's dead-letter queue and alert HR channel. Do not proceed to downstream steps that depend on the write until it is confirmed successful.
Notion API
Page creation fails (rate limit or server error)
Retry with exponential backoff: wait 5s, 15s, 45s. Maximum 3 retries. If all retries fail, export the full AI narrative and scores as a plain-text fallback and email directly to GMAIL_SENDING_ADDRESS with subject 'MANUAL ACTION: Notion report creation failed for {cycle_date}'. Log failure and alert HR channel.
Notion API
Status poll for 'Approved' times out (HR has not reviewed within 48 hours)
Send a reminder to SLACK_HR_CHANNEL_ID: 'The survey report for {cycle_date} is awaiting your review in Notion. Distribution is on hold.' Repeat reminder at 72 hours. After 96 hours with no approval, log a timeout warning and halt the distribution step. Do not auto-approve or auto-distribute.
Slack API
Message post fails (invalid channel ID or bot not in channel)
Log error with channel ID and HTTP response. Do not retry more than once. If the leadership channel post fails post-approval, fall back to sending the Gmail distribution email to the leadership list regardless. Log that Slack notification was not delivered. Alert HR channel if the HR channel post itself fails (use secondary webhook URL SLACK_HR_WEBHOOK_URL as fallback).
Unhandled exceptions policy: any exception not covered by the table above must be caught by the orchestration platform's global error handler, logged with full execution context (workflow name, step name, input payload hash, timestamp), and routed to an alert email to support@gofullspec.com and to SLACK_HR_CHANNEL_ID. The workflow must halt at the point of failure. Silent failures are not acceptable under any circumstance.
Integration and API SpecPage 4 of 4

More documents for this process

Every document generated for Employee Satisfaction & Pulse Surveys.

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