Back to Workplace Incident Reporting

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

Workplace Incident Reporting

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

This document is the definitive technical reference for every integration point in the Workplace Incident Reporting automation. It covers the full tool inventory, exact OAuth scopes, webhook configuration, field mappings between tools, credential store layout, orchestration design, and error handling requirements for each integration. The FullSpec team uses this specification to build, configure, and validate all connections. Nothing in this document should require interpretation: where a field name, scope string, or retry interval is needed, the exact value is stated.

01Tool inventory

Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
Orchestration layer
Workflow automation platform; hosts all three agent workflows, manages scheduling, credential store, and inter-agent triggers
N/A (internal)
Any plan supporting webhooks + scheduled polling
All agents
Google Forms
Incident intake: employee-facing form that triggers the entire workflow on submission
OAuth 2.0 (Google account)
Free (Google Workspace or personal Google account)
Agent 1
Google Sheets
Incident register: structured row written by Agent 1, read by Agents 2 and 3 as trigger source
OAuth 2.0 (Google account)
Free (Google Workspace or personal Google account)
Agents 1, 2, 3
Slack
Manager and escalation notifications: Agent 2 posts incident alerts and direct messages
OAuth 2.0 (Slack app)
Pro ($8/user/month) for reliable incoming webhooks and DM to non-admins
Agent 2
Notion
Investigation tasks and corrective action tracking: Agent 3 creates and monitors database pages
OAuth 2.0 (Notion integration token)
Plus ($16/user/month) for API access and database automation
Agent 3
Gmail
Deadline reminder emails and closure confirmation emails sent by Agent 3
OAuth 2.0 (Google account)
Free (same Google Workspace account as Forms/Sheets)
Agent 3
BambooHR
Employee record updates for injury-classified incidents: Agent 3 writes incident date, type, and reference
API key (HTTP header)
Essentials or Advantage plan with API access enabled
Agent 3
Before you connect anything: create sandbox or test credentials for every tool in this inventory and validate each connection in a non-production environment before any production credentials are entered. For Google Workspace, use a dedicated service account. For BambooHR, request a separate API key scoped to a test subdomain. For Slack, install the app to a private test channel first. Only after all connections pass the integration tests in Doc 6 should production credentials be stored.
Integration and API SpecPage 1 of 4
FS-DOC-05Technical

02Per-tool integration detail

Google Forms

Incident intake trigger. The form is the sole entry point for all incident reports. A new form response fires the orchestration layer's trigger step and passes the raw response payload to Agent 1.

Auth method
OAuth 2.0. The orchestration layer authenticates as a Google account with Forms and Sheets access. Use a dedicated service account (not a personal login) so credentials survive staff changes.
Required scopes
https://www.googleapis.com/auth/forms.responses.readonly | https://www.googleapis.com/auth/drive.readonly
Webhook / trigger setup
Use the Google Forms API v1 watch endpoint (POST /v1/forms/{formId}/watches) to register a RESPONSE_CREATED watch. The orchestration layer receives a push notification to its inbound webhook URL within seconds of submission. Alternatively, poll the responses endpoint every 2 minutes if the platform does not support outbound watch registration. Watch expiry is 7 days; the platform must auto-renew via POST /v1/forms/{formId}/watches/{watchId}:renew on a 6-day schedule.
Required configuration
Store the formId in the credential store (key: GFORMS_INCIDENT_FORM_ID). Do not hardcode. Confirm all required question IDs map to the field mapping table in Section 03.
Rate limits
Google Forms API: 300 read requests per minute per project. At 12 incidents/month (approximately 0.4 submissions/day), throttling is not required. Maintain exponential backoff on 429 responses regardless.
Constraints
Watch notifications do not include the response body; a follow-up GET /v1/forms/{formId}/responses/{responseId} call is required to fetch field values. Ensure the orchestration step fetches the full response immediately after the watch fires.
// Inbound watch notification payload (abbreviated)
{
  "formId": "<GFORMS_INCIDENT_FORM_ID>",
  "responseId": "<string>",
  "eventType": "RESPONSE_CREATED"
}

// Follow-up GET returns full response
GET https://forms.googleapis.com/v1/forms/{formId}/responses/{responseId}
Authorization: Bearer <oauth_access_token>
Google Sheets

Incident register. Agent 1 appends a new structured row on each submission. Agents 2 and 3 are triggered by detecting that new row. Agent 3 also updates the row status field when the incident is closed.

Auth method
OAuth 2.0. Same service account credential as Google Forms. No additional auth step required.
Required scopes
https://www.googleapis.com/auth/spreadsheets | https://www.googleapis.com/auth/drive.readonly
Webhook / trigger setup
Google Sheets does not natively push events. Agents 2 and 3 poll the sheet on a 2-minute interval using GET /v4/spreadsheets/{spreadsheetId}/values/{range} and compare against a stored last_processed_row index. Alternatively, Agent 1 can post a message to an internal queue after writing, and Agents 2 and 3 subscribe to that queue event. The latter is preferred for lower latency.
Required configuration
Store spreadsheetId as GSHEETS_INCIDENT_REGISTER_ID in the credential store. Store the named range as GSHEETS_INCIDENT_RANGE (e.g. IncidentRegister!A:Z). Column headers must match exactly the field mapping table in Section 03. The sheet must be pre-structured with a header row before the first automation run.
Rate limits
Sheets API v4: 300 requests per minute per project, 60 requests per minute per user. At 12 incidents/month with 2-minute polling, peak load is approximately 30 polling calls per hour, well within limits. No throttling required at current volume.
Constraints
Append only via POST /v4/spreadsheets/{spreadsheetId}/values/{range}:append with valueInputOption=USER_ENTERED. Do not use batchUpdate for row appends as it risks overwriting existing data. Status updates (closure) use PUT /v4/spreadsheets/{spreadsheetId}/values/{range} targeting the specific row by reference number lookup.
// Append new incident row
POST https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}/values/{range}:append
?valueInputOption=USER_ENTERED

// Body
{
  "values": [[
    "<reference_number>", "<submission_timestamp>", "<reporter_name>",
    "<department>", "<incident_type>", "<severity>", "<description>",
    "<injured_employee_id>", "<manager_email>", "open", ""
  ]]
}
Slack

Manager and escalation notifications. Agent 2 posts a channel message to the relevant department manager channel and, for serious incidents, sends a direct message to the Operations Director.

Auth method
OAuth 2.0 via Slack app. Install a custom Slack app in the workspace and grant it to the orchestration layer. Store the bot token as SLACK_BOT_TOKEN in the credential store.
Required scopes
chat:write | im:write | users:read | users:read.email | channels:read | groups:read
Webhook / trigger setup
No inbound webhook from Slack is required for this automation. All Slack interactions are outbound (platform to Slack). Use the Slack Web API POST /api/chat.postMessage for channel messages and POST /api/chat.postMessage with the manager's user ID as the channel value for direct messages.
Required configuration
Store a department-to-channel mapping in the credential store as a JSON object (key: SLACK_DEPT_CHANNEL_MAP). Store the Operations Director's Slack user ID as SLACK_OPS_DIRECTOR_USER_ID. Do not hardcode channel IDs or user IDs in workflow steps. Example mapping structure: {"operations": "C012AB3CD", "retail": "C045EF6GH"}. Confirm channel IDs via GET /api/conversations.list before go-live.
Rate limits
Slack Web API tier 3: 50 requests per minute for chat.postMessage. At 12 incidents/month, peak is 2 messages per incident (channel + possible DM), totalling approximately 24 Slack API calls per month. No throttling required.
Constraints
Bot must be invited to every department channel listed in SLACK_DEPT_CHANNEL_MAP before messages can be posted. If a channel ID is not found in the mapping, the agent must fall back to posting to a default HR alert channel (SLACK_FALLBACK_CHANNEL_ID) and logging the routing failure. Never fail silently on a missing channel mapping.
// Post channel notification
POST https://slack.com/api/chat.postMessage
Authorization: Bearer <SLACK_BOT_TOKEN>

// Body
{
  "channel": "<dept_channel_id>",
  "text": "Incident <reference_number> logged. Severity: <severity>. Reporter: <reporter_name>.",
  "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "*<ref>* | <severity> | <summary>" } } ]
}

// Direct message for serious incidents
{
  "channel": "<SLACK_OPS_DIRECTOR_USER_ID>",
  "text": "SERIOUS incident <reference_number> requires your attention. Dept: <department>."
}
Notion

Investigation tasks and corrective action tracking. Agent 3 creates a new database page per incident, sets structured properties, and polls for status changes to detect overdue investigations and closed records.

Auth method
OAuth 2.0 integration token. Create an internal Notion integration at notion.so/my-integrations and share the investigation database with it. Store the secret as NOTION_INTEGRATION_TOKEN. Store the database ID as NOTION_INVESTIGATION_DB_ID.
Required scopes
Notion integration capabilities required: read content, update content, insert content. These are set at integration creation time, not via OAuth scope strings. Ensure all three capabilities are enabled.
Webhook / trigger setup
Notion does not offer native outbound webhooks at this time. Agent 3 polls the investigation database on a 15-minute schedule using POST /v1/databases/{database_id}/query filtered by last_edited_time >= last_poll_timestamp. This detects both new pages (created externally) and status changes made by HR in Notion.
Required configuration
The Notion investigation database must be pre-created with the following properties matching the field mapping table: Reference Number (title), Incident Date (date), Severity (select: Minor, Moderate, Serious), Department (select), Assigned Manager (email), Investigation Deadline (date), Status (select: Open, In Progress, Overdue, Closed), Findings (rich text), Corrective Actions (rich text), BambooHR Updated (checkbox). Store NOTION_INVESTIGATION_DB_ID in the credential store. Do not hardcode property names; store them as NOTION_PROP_* constants.
Rate limits
Notion API: 3 requests per second per integration. At 15-minute polling intervals and 12 incidents/month, sustained load is negligible. No throttling required at current volume. Apply 1-second delay between sequential page create and property update calls within a single workflow run.
Constraints
Notion page titles must be non-empty. Always set the Reference Number property as the page title. If the Notion API returns a 409 conflict (duplicate page), log the incident reference and skip creation rather than creating a duplicate. The integration token must have explicit access to the database; workspace-wide access is not granted by default.
// Create investigation page
POST https://api.notion.com/v1/pages
Authorization: Bearer <NOTION_INTEGRATION_TOKEN>
Notion-Version: 2022-06-28

// Body (abbreviated)
{
  "parent": { "database_id": "<NOTION_INVESTIGATION_DB_ID>" },
  "properties": {
    "Reference Number": { "title": [{ "text": { "content": "<ref_number>" } }] },
    "Severity": { "select": { "name": "<severity>" } },
    "Investigation Deadline": { "date": { "start": "<deadline_iso8601>" } },
    "Status": { "select": { "name": "Open" } }
  }
}
Gmail

Deadline reminder emails and closure confirmation emails. Agent 3 sends outbound email via the Gmail API on two triggers: investigation deadline approaching or passed (reminder), and incident closed in Notion (closure confirmation to reporter).

Auth method
OAuth 2.0. The orchestration layer authenticates as the HR Gmail account. Use the same service account as Google Forms and Sheets with domain-wide delegation enabled, or authenticate as the HR coordinator's Google account directly. Store the refresh token as GMAIL_OAUTH_REFRESH_TOKEN.
Required scopes
https://www.googleapis.com/auth/gmail.send
Webhook / trigger setup
Gmail is outbound only in this workflow. No Gmail push or polling is required. Sends are triggered by Agent 3's internal logic (Notion deadline check and Notion status change detection).
Required configuration
Store the sender address as GMAIL_SENDER_ADDRESS. Store reminder email template IDs as GMAIL_TEMPLATE_REMINDER and GMAIL_TEMPLATE_CLOSURE. Templates must contain the following placeholders: {{reference_number}}, {{manager_name}}, {{incident_date}}, {{investigation_deadline}}, {{notion_page_url}}, {{reporter_name}}. Do not inline template content in workflow steps.
Rate limits
Gmail API sending quota: 100 emails per second per user, 1 billion sends per day (Google Workspace). At 12 incidents/month with at most 3 reminder emails per incident, monthly volume is approximately 36 emails. No throttling required.
Constraints
Email must be constructed as a base64url-encoded RFC 2822 message and passed to POST /gmail/v1/users/me/messages/send as the raw field. Do not use SMTP directly. If a send fails, log the failure and retry with backoff; do not swallow the error silently.
// Send email via Gmail API
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
Authorization: Bearer <oauth_access_token>

// Body
{
  "raw": "<base64url_encoded_RFC2822_message>"
}

// RFC 2822 headers required
From: <GMAIL_SENDER_ADDRESS>
To: <manager_email | reporter_email>
Subject: Incident <reference_number> - Investigation Reminder
Content-Type: text/html; charset=UTF-8
BambooHR

Employee record updates for injury-classified incidents. Agent 3 writes the incident date, type, and reference number to the employee's custom fields in BambooHR. This step runs only when the severity classification includes an injury flag.

Auth method
API key authentication. BambooHR uses HTTP Basic Auth with the API key as the username and any string as the password. Store the key as BAMBOOHR_API_KEY and the company subdomain as BAMBOOHR_SUBDOMAIN. Base URL: https://api.bamboohr.com/api/gateway.php/{subdomain}/v1/
Required scopes
BambooHR does not use OAuth scopes. The API key must belong to an account with the following permissions: Employees - View and Edit. Confirm the key's permission set in BambooHR Admin before build. If a read-only key is provided, field updates will fail with 403.
Webhook / trigger setup
No inbound webhook from BambooHR is required. BambooHR is a write-only integration target in this workflow. The update call is triggered by Agent 3's internal logic when is_injury == true.
Required configuration
Store BAMBOOHR_INCIDENT_DATE_FIELD_ID, BAMBOOHR_INCIDENT_TYPE_FIELD_ID, and BAMBOOHR_INCIDENT_REF_FIELD_ID in the credential store. These are numeric custom field IDs visible in BambooHR Settings > Custom Fields. Do not hardcode field IDs. Confirm all three custom fields exist in the BambooHR account before build; create them if absent. Store BAMBOOHR_SUBDOMAIN separately from the API key.
Rate limits
BambooHR API: 1 request per second per API key (hard limit enforced by a 429 response). At 12 incidents/month, the update step fires at most 12 times per month. No throttling logic is required beyond a mandatory 1-second delay before each BambooHR API call to avoid burst rate violations.
Constraints
The employee ID required for PUT /v1/employees/{id}/customTabFields/{tabId} must be resolved from the form's submitted employee identifier. If the form captures an email address, use GET /v1/employees/directory to look up the employee ID by email before the update call. Cache the mapping during the workflow run. If the employee is not found, log the error and notify HR via Slack rather than failing the entire workflow.
// Update employee custom fields
PUT https://api.bamboohr.com/api/gateway.php/{BAMBOOHR_SUBDOMAIN}/v1/employees/{employee_id}/customTabFields/{tabId}
Authorization: Basic <base64(BAMBOOHR_API_KEY:x)>
Content-Type: application/json
Accept: application/json

// Body
{
  "customField": [
    { "fieldId": "<BAMBOOHR_INCIDENT_DATE_FIELD_ID>",  "value": "<incident_date_YYYY-MM-DD>" },
    { "fieldId": "<BAMBOOHR_INCIDENT_TYPE_FIELD_ID>",  "value": "<incident_type>" },
    { "fieldId": "<BAMBOOHR_INCIDENT_REF_FIELD_ID>",   "value": "<reference_number>" }
  ]
}
Integration and API SpecPage 2 of 4
FS-DOC-05Technical

03Field mappings between tools

The following tables cover every field handoff between tools at each agent boundary. Field names in monospace reflect the exact property keys, column headers, or API field identifiers used in each tool. Any deviation from these exact names will cause a mapping failure.

Agent 1 handoff: Google Forms to Google Sheets (Incident Intake Agent)

Source tool
Source field
Destination tool
Destination field
Google Forms
`answers[q_reporter_name].textAnswers.answers[0].value`
Google Sheets
`reporter_name`
Google Forms
`answers[q_reporter_email].textAnswers.answers[0].value`
Google Sheets
`reporter_email`
Google Forms
`answers[q_incident_date].textAnswers.answers[0].value`
Google Sheets
`incident_date`
Google Forms
`answers[q_incident_time].textAnswers.answers[0].value`
Google Sheets
`incident_time`
Google Forms
`answers[q_location].textAnswers.answers[0].value`
Google Sheets
`incident_location`
Google Forms
`answers[q_description].textAnswers.answers[0].value`
Google Sheets
`description`
Google Forms
`answers[q_incident_type].choiceAnswers.values[0]`
Google Sheets
`incident_type`
Google Forms
`answers[q_injury_involved].choiceAnswers.values[0]`
Google Sheets
`is_injury`
Google Forms
`answers[q_injured_employee_id].textAnswers.answers[0].value`
Google Sheets
`injured_employee_id`
Google Forms
`answers[q_department].choiceAnswers.values[0]`
Google Sheets
`department`
Google Forms
`answers[q_manager_email].textAnswers.answers[0].value`
Google Sheets
`manager_email`
Orchestration layer (computed)
`severity_classification`
Google Sheets
`severity`
Orchestration layer (computed)
`reference_number`
Google Sheets
`reference_number`
Orchestration layer (computed)
`submission_timestamp`
Google Sheets
`logged_at`
Orchestration layer (static)
`"open"`
Google Sheets
`status`

Agent 2 handoff: Google Sheets to Slack (Notification and Escalation Agent)

Source tool
Source field
Destination tool
Destination field
Google Sheets
`reference_number`
Slack
`text` (message body)
Google Sheets
`severity`
Slack
`text` (message body); routing condition for DM escalation
Google Sheets
`department`
Slack
`channel` (resolved via SLACK_DEPT_CHANNEL_MAP)
Google Sheets
`manager_email`
Slack
`text` (mention via email lookup to Slack user ID)
Google Sheets
`description`
Slack
`blocks[].text.text` (summary field)
Google Sheets
`incident_type`
Slack
`blocks[].text.text` (incident type field)
Google Sheets
`incident_date`
Slack
`blocks[].text.text` (date field)
Orchestration layer (computed)
`notified_at`
Google Sheets
`notification_sent_at` (written back after send)

Agent 3 handoff: Google Sheets to Notion (Investigation and Corrective Action Agent)

Source tool
Source field
Destination tool
Destination field
Google Sheets
`reference_number`
Notion
`Reference Number` (title property)
Google Sheets
`incident_date`
Notion
`Incident Date` (date property)
Google Sheets
`severity`
Notion
`Severity` (select property)
Google Sheets
`department`
Notion
`Department` (select property)
Google Sheets
`manager_email`
Notion
`Assigned Manager` (email property)
Orchestration layer (computed)
`investigation_deadline`
Notion
`Investigation Deadline` (date property; incident_date + 5 business days for Moderate, + 2 for Serious, + 10 for Minor)
Orchestration layer (static)
`"Open"`
Notion
`Status` (select property)
Google Sheets
`description`
Notion
`Findings` (rich text property; pre-populated with submission text)
Orchestration layer (static)
`false`
Notion
`BambooHR Updated` (checkbox property)

Agent 3 handoff: Notion to Gmail (reminder and closure emails)

Source tool
Source field
Destination tool
Destination field
Notion
`Reference Number`
Gmail
`subject` (email subject line placeholder {{reference_number}})
Notion
`Assigned Manager` (email)
Gmail
`To` header
Notion
`Investigation Deadline`
Gmail
`body` placeholder {{investigation_deadline}}
Notion
`page_url` (computed by Notion API response)
Gmail
`body` placeholder {{notion_page_url}}
Google Sheets
`reporter_email`
Gmail
`To` header (closure confirmation email only)
Google Sheets
`reporter_name`
Gmail
`body` placeholder {{reporter_name}}

Agent 3 handoff: Google Sheets to BambooHR (injury incident record update)

Source tool
Source field
Destination tool
Destination field
Google Sheets
`injured_employee_id` (email; resolved to BambooHR employee ID via directory lookup)
BambooHR
`employee_id` (URL path parameter)
Google Sheets
`incident_date`
BambooHR
Custom field `BAMBOOHR_INCIDENT_DATE_FIELD_ID`
Google Sheets
`incident_type`
BambooHR
Custom field `BAMBOOHR_INCIDENT_TYPE_FIELD_ID`
Google Sheets
`reference_number`
BambooHR
Custom field `BAMBOOHR_INCIDENT_REF_FIELD_ID`
Orchestration layer (written back)
`"true"`
Notion
`BambooHR Updated` (checkbox; set after successful PUT)
Integration and API SpecPage 3 of 4
FS-DOC-05Technical

04Build stack and orchestration

Orchestration layout
Three discrete workflows, one per agent. Each workflow is self-contained with its own trigger, credential references, and error handler. Workflows share a single credential store; no credentials are duplicated across workflows.
Agent 1 trigger mechanism
Webhook (push): Google Forms watch notification sent to the orchestration layer's inbound webhook URL. Fallback: poll Google Forms responses endpoint every 2 minutes if the watch lapses. Watch renewal runs on a 6-day cron schedule via a separate maintenance workflow.
Agent 1 trigger signature validation
Google Forms watch notifications arrive as HTTP POST with no built-in signature. Restrict the inbound webhook URL to Google's published IP ranges (fetch dynamically from https://www.gstatic.com/ipranges/goog.json). Log and reject requests from any other origin.
Agent 2 trigger mechanism
Poll (internal queue or Google Sheets poll): Agent 2 watches for new rows in the Google Sheets incident register. Preferred approach is an internal event queue written to by Agent 1 at step completion. Alternative: poll Sheets every 2 minutes, compare row count against GSHEETS_LAST_PROCESSED_ROW stored in the platform's key-value store.
Agent 3 trigger mechanism
Poll: Agent 3 runs on a 15-minute scheduled poll. It queries the Notion investigation database for pages with Status != Closed and checks investigation deadlines and corrective action states. It also subscribes to the same internal event queue as Agent 2 for immediate task creation on new incident rows.
Shared credential store
All secrets are stored in the orchestration platform's encrypted credential store. No plaintext secret appears in any workflow step, environment variable export, or log entry. Credential references use the key names listed in the code block below.
Inter-agent communication
Agent 1 writes a completed incident row to Google Sheets and optionally publishes an event to an internal queue (key: incident_reference_number). Agents 2 and 3 consume this event. No direct function calls between agent workflows.
Logging
Each workflow step logs: step name, timestamp, input payload hash (not plaintext PII), success/failure status, and any API response code. Logs are retained for 90 days minimum for audit purposes.
All keys stored in the orchestration platform's encrypted credential store. No value is hardcoded in any workflow step.
// Credential store keys
// Google
GFORMS_INCIDENT_FORM_ID          = "<google_forms_form_id>"
GSHEETS_INCIDENT_REGISTER_ID     = "<spreadsheet_id>"
GSHEETS_INCIDENT_RANGE           = "IncidentRegister!A:Z"
GOOGLE_OAUTH_CLIENT_ID           = "<oauth_client_id>"
GOOGLE_OAUTH_CLIENT_SECRET       = "<oauth_client_secret>"
GOOGLE_OAUTH_REFRESH_TOKEN       = "<oauth_refresh_token>"
GMAIL_SENDER_ADDRESS             = "<hr@yourcompany.com>"
GMAIL_TEMPLATE_REMINDER          = "<template_id_or_path>"
GMAIL_TEMPLATE_CLOSURE           = "<template_id_or_path>"

// Slack
SLACK_BOT_TOKEN                  = "xoxb-<token>"
SLACK_DEPT_CHANNEL_MAP           = "{\"operations\": \"C012AB3CD\", \"retail\": \"C045EF6GH\"}"
SLACK_OPS_DIRECTOR_USER_ID       = "U0123ABCDE"
SLACK_FALLBACK_CHANNEL_ID        = "C099ZZZZZZ"
SLACK_HR_ALERT_CHANNEL_ID        = "C077HRALRT"

// Notion
NOTION_INTEGRATION_TOKEN         = "secret_<token>"
NOTION_INVESTIGATION_DB_ID       = "<32_char_database_id>"
NOTION_PROP_REFERENCE_NUMBER     = "Reference Number"
NOTION_PROP_SEVERITY             = "Severity"
NOTION_PROP_DEADLINE             = "Investigation Deadline"
NOTION_PROP_STATUS               = "Status"
NOTION_PROP_ASSIGNED_MANAGER     = "Assigned Manager"
NOTION_PROP_BAMBOOHR_UPDATED     = "BambooHR Updated"

// BambooHR
BAMBOOHR_API_KEY                 = "<api_key>"
BAMBOOHR_SUBDOMAIN               = "<yourcompany>"
BAMBOOHR_INCIDENT_DATE_FIELD_ID  = "<numeric_field_id>"
BAMBOOHR_INCIDENT_TYPE_FIELD_ID  = "<numeric_field_id>"
BAMBOOHR_INCIDENT_REF_FIELD_ID   = "<numeric_field_id>"
BAMBOOHR_CUSTOM_TAB_ID           = "<numeric_tab_id>"

// Orchestration internal
GSHEETS_LAST_PROCESSED_ROW       = "<integer; updated after each Agent 2/3 poll>"
The SLACK_DEPT_CHANNEL_MAP value must be validated before go-live. Retrieve all channel IDs via GET /api/conversations.list and cross-reference against the department list in the Google Form's dropdown options. Any department that appears in the form but is absent from the map will cause a routing failure at runtime.

05Error handling and retry logic

Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently. All retry sequences use exponential backoff starting at 10 seconds unless otherwise stated. After the final retry attempt, the workflow must log the failure and trigger a human alert via the SLACK_HR_ALERT_CHANNEL_ID channel with the incident reference number, the failing step name, and the error code.

Integration
Scenario
Required behaviour
Google Forms watch registration
Watch registration fails on startup or renewal
Retry registration 3 times with 30-second backoff. On third failure, switch to 2-minute polling fallback automatically and post alert to SLACK_HR_ALERT_CHANNEL_ID. Log the failure. Do not halt other workflows.
Google Forms response fetch
GET /responses/{responseId} returns 404 or 5xx after watch notification
Retry 3 times with 10s/30s/60s backoff. If all retries fail, log the responseId and post alert to SLACK_HR_ALERT_CHANNEL_ID with the raw watch payload for manual recovery. Do not drop the event.
Google Sheets row append
POST :append returns 429 (quota exceeded)
Wait 60 seconds then retry up to 3 times. If all retries fail, store the incident payload in the platform's internal key-value store and retry on the next scheduled poll cycle. Post alert to SLACK_HR_ALERT_CHANNEL_ID.
Google Sheets row append
POST :append returns 5xx (Google service outage)
Retry 3 times with 30s/60s/120s backoff. On final failure, store payload locally and attempt re-send every 5 minutes for up to 1 hour. If unresolved after 1 hour, escalate to SLACK_HR_ALERT_CHANNEL_ID and log for manual entry.
Slack chat.postMessage
Channel ID not found in SLACK_DEPT_CHANNEL_MAP for incoming department value
Do not drop the notification. Immediately fall back to posting to SLACK_FALLBACK_CHANNEL_ID with the full incident summary and a note that routing failed. Log the unmatched department value. Do not retry the original channel.
Slack chat.postMessage
API returns 429 or 5xx
Retry up to 3 times with 10s/30s/60s backoff. If all retries fail, send the manager notification via Gmail as a fallback (use manager_email from Sheets) and log the Slack failure. Post a separate alert to SLACK_HR_ALERT_CHANNEL_ID once Slack is available again.
Notion page creation
POST /pages returns 409 (page already exists for reference number)
Log the duplicate attempt and skip creation. Query the database for the existing page ID and continue the workflow using that existing page. Do not create a duplicate. Post a warning to SLACK_HR_ALERT_CHANNEL_ID.
Notion page creation
POST /pages returns 401 (invalid token) or 403 (no database access)
Do not retry. Post an immediate alert to SLACK_HR_ALERT_CHANNEL_ID and halt Agent 3 workflow for this incident. Require manual credential verification before the workflow is re-enabled. Log the error with full response body.
Gmail send (reminder)
POST /messages/send returns 4xx or 5xx
Retry 3 times with 15s/45s/90s backoff. If all retries fail, log the intended recipient and subject line and post alert to SLACK_HR_ALERT_CHANNEL_ID so HR can send the reminder manually. Never suppress a missed reminder silently.
BambooHR employee directory lookup
Employee email not found in directory (GET /employees/directory returns no match)
Do not attempt the field update. Log the unmatched email and post an alert to SLACK_HR_ALERT_CHANNEL_ID with the incident reference and the unmatched identifier. HR must resolve the employee ID manually. The rest of the workflow continues unaffected.
BambooHR custom field update
PUT returns 403 (insufficient API key permissions)
Do not retry. Post an immediate alert to SLACK_HR_ALERT_CHANNEL_ID and log the full error. Flag the incident row in Google Sheets (status column: bamboohr_update_failed). Require admin to verify API key permissions before re-running the step.
BambooHR custom field update
PUT returns 429 (rate limit; 1 request/second exceeded)
Wait 2 seconds and retry once. If second attempt fails, queue the update and retry after 60 seconds. Apply a mandatory 1-second pre-call delay to all BambooHR API calls within the workflow to prevent repeat violations.
All error alerts posted to SLACK_HR_ALERT_CHANNEL_ID must include: the incident reference number (if known), the workflow and step name that failed, the HTTP status code or error message received, and the timestamp. Generic error messages are not acceptable. The HR coordinator must be able to take manual action from the alert alone without needing to inspect workflow logs.
Integration and API SpecPage 4 of 4

More documents for this process

Every document generated for Workplace Incident Reporting.

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