FS-DOC-05Technical
Integration and API Spec
User Provisioning and Access Management
[YourCompany.com] · IT Department · Prepared by FullSpec · [Today's Date]
This document defines every integration point, authentication method, required scope, field mapping, and error-handling behaviour for the User Provisioning and Access Management automation. It is written for the FullSpec build team and covers the two agents in this workflow: the Provisioning Policy Agent (Agent 1) and the Offboarding Audit Agent (Agent 2). All credential storage, retry logic, and orchestration decisions described here must be followed exactly during build. Where a detail is not specified in the confirmed process brief, realistic and internally consistent defaults appropriate to each named tool are stated.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Automation platform (orchestration layer)
Hosts both agent workflows, manages the credential store, executes polling and webhook listeners
N/A (internal)
N/A
Both
BambooHR
HRIS trigger source: fires on employee status change (New Hire, Transfer, Terminated)
API key (Basic Auth over HTTPS)
Essentials or higher (webhook support required)
Agent 1
Okta
Central identity provider: create, update, suspend, and deactivate user accounts; group membership management
OAuth 2.0 (Client Credentials) or API token
Okta Workforce Identity (any paid tier)
Agent 1, Agent 2
Google Workspace
Email and Drive account provisioning; OU assignment; group membership; file ownership transfer on offboarding
OAuth 2.0 (service account with domain-wide delegation)
Business Starter or higher
Agent 1, Agent 2
Microsoft 365
Licence assignment; Teams and SharePoint group membership; mailbox archiving on offboarding
OAuth 2.0 (Azure AD app registration, Client Credentials flow)
Microsoft 365 Business Basic or higher
Agent 1, Agent 2
Slack
Workspace invite and channel membership on hire; deactivation and channel removal on offboarding; manager notifications
OAuth 2.0 (bot token via Slack app installation)
Pro or higher (SCIM API requires Business+ for full deactivation)
Agent 1, Agent 2
Jira
Audit ticket creation with timestamped deactivation log; elevated-permissions review issue creation
API token (Basic Auth: user email + token)
Jira Cloud Standard or higher
Agent 2
Before you connect anything: all six integrations must be tested against sandbox or development environments before any production credentials are entered. BambooHR, Okta, and Microsoft 365 all offer sandbox tenants or test organisations. Google Workspace testing must use a non-production domain or a dedicated test OU. Slack app connections must use a separate test workspace. Jira connections must use a test project. Do not write production credentials into any workflow step until sandbox testing has passed QA.
Integration and API SpecPage 1 of 4
FS-DOC-05Technical
02Per-tool integration detail
BambooHR
BambooHR is the authoritative trigger source for all provisioning and deprovisioning events. The automation platform registers a webhook endpoint in BambooHR that fires on any employee record status change. The payload is parsed to extract the employee ID, status, role, and department before the Provisioning Policy Agent proceeds.
Auth method
API key passed as the username in HTTP Basic Auth over HTTPS. Password field is left empty. Key is stored in the credential store as BAMBOOHR_API_KEY.
Required scopes
BambooHR API keys inherit the permissions of the generating user. The service account used must have read access to: Employee directory fields (employeeNumber, firstName, lastName, jobTitle, department, location, supervisorId, hireDate, terminationDate, employmentStatus). No OAuth scopes apply; permission is role-based on the BambooHR admin console.
Webhook setup
Navigate to BambooHR Admin > API > Webhooks. Create a new webhook pointing to the automation platform inbound endpoint. Select trigger event: Employee Changed. Monitor fields: employmentStatus, hireDate, terminationDate. Set Content-Type to application/json. BambooHR signs webhook payloads with an HMAC-SHA256 signature in the X-BambooHR-Signature-V2 header. The automation platform must validate this signature against BAMBOOHR_WEBHOOK_SECRET before processing any payload.
Required configuration
Company subdomain stored as BAMBOOHR_SUBDOMAIN in the credential store. All API calls use the base URL: https://api.bamboohr.com/api/gateway.php/{BAMBOOHR_SUBDOMAIN}/v1/. Employee field IDs for custom fields (e.g. access_tier, cost_centre) must be confirmed with the BambooHR admin and stored in the workflow config, not hardcoded.
Rate limits
BambooHR enforces a limit of approximately 500 API requests per hour per API key. At a volume of 6 to 10 provisioning events per month, peak load is well under 100 API calls per event. No throttling logic is required at current volume, but exponential backoff must be implemented for any 429 response.
Constraints
BambooHR webhooks do not guarantee delivery order for rapid successive changes. If two status changes fire within seconds, the automation platform must deduplicate by employeeNumber and processedAt timestamp. The webhook payload does not include all employee fields; a follow-up GET /employees/{id} call is always required to fetch the full record.
// Webhook payload (abbreviated)
{
"employeeId": "1042",
"action": "changed",
"fields": {
"employmentStatus": { "oldValue": "Active", "newValue": "Terminated" }
}
}
// Follow-up GET response fields consumed by Agent 1
id, firstName, lastName, jobTitle, department, location,
supervisorId, hireDate, terminationDate, employmentStatus,
customField_access_tier, customField_cost_centreOkta
Okta is the central identity provider. Agent 1 creates or updates Okta user profiles and assigns group memberships on hire or transfer. Agent 2 immediately suspends and then deactivates the Okta account on termination. Group membership in Okta drives federated access to Google Workspace and Microsoft 365 where SAML or OIDC federation is configured.
Auth method
OAuth 2.0 Client Credentials flow using an Okta service app. Client ID stored as OKTA_CLIENT_ID; client secret stored as OKTA_CLIENT_SECRET; Okta org domain stored as OKTA_DOMAIN.
Required scopes
okta.users.manage, okta.groups.manage, okta.groups.read, okta.users.read. Scopes are granted at the service app level in the Okta Admin console under Applications > Service App > Okta API Scopes.
Webhook / trigger
Okta is not a trigger source in this workflow. It is an action target only. No inbound webhook is registered in Okta for this automation.
Required configuration
Okta group IDs for every role-based access group must be stored in the workflow config (e.g. GROUP_ID_ENGINEERING, GROUP_ID_SALES) and not hardcoded inline. The role-to-group mapping table is maintained by the IT administrator as a config variable. Okta org URL format: https://{OKTA_DOMAIN}/api/v1/.
Rate limits
Okta enforces per-endpoint rate limits. Users API: 600 requests/minute for create/update; Groups API: 300 requests/minute. At 6 to 10 events per month, no throttling is needed. Implement retry with exponential backoff on HTTP 429, respecting the X-Rate-Limit-Reset header.
Constraints
Suspension must precede deactivation on offboarding. Calling deactivate directly without suspending first can cause federation sync delays in Google Workspace. Deactivation is irreversible via API; reactivation requires creating a new user record. For transfers, update the user profile and group memberships without deactivating.
// POST /api/v1/users?activate=true (hire)
{
"profile": {
"firstName": "{{firstName}}",
"lastName": "{{lastName}}",
"email": "{{workEmail}}",
"login": "{{workEmail}}",
"department": "{{department}}",
"title": "{{jobTitle}}"
},
"groupIds": ["{{resolved_group_ids}}"]
}
// POST /api/v1/users/{userId}/lifecycle/suspend (offboarding step 1)
// POST /api/v1/users/{userId}/lifecycle/deactivate (offboarding step 2)Google Workspace
Google Workspace provisioning covers user account creation, organisational unit assignment, Google Group membership, and shared Drive access on hire. On offboarding, the account is suspended, Drive files are transferred to the line manager, and the mailbox is delegated or archived per the data retention policy agreed with legal.
Auth method
OAuth 2.0 with a Google Cloud service account and domain-wide delegation (DWD). Service account JSON key stored as GOOGLE_SA_KEY_JSON in the credential store. DWD must be granted in the Google Workspace Admin console under Security > API Controls > Domain-wide Delegation.
Required scopes
https://www.googleapis.com/auth/admin.directory.user, https://www.googleapis.com/auth/admin.directory.group, https://www.googleapis.com/auth/admin.directory.orgunit, https://www.googleapis.com/auth/drive (for ownership transfer), https://www.googleapis.com/auth/gmail.settings.sharing (for mailbox delegation). All scopes granted via DWD to the service account impersonating an admin user (email stored as GOOGLE_ADMIN_EMAIL).
Webhook / trigger
Not a trigger source. Action target only. Google Workspace Admin SDK push notifications are not used in this workflow.
Required configuration
Target OU paths per department stored in the workflow config (e.g. OU_ENGINEERING=/corp/engineering). Primary domain stored as GOOGLE_PRIMARY_DOMAIN. Google Group IDs for role-based groups stored as config variables. The line manager's email is resolved from the BambooHR supervisorId before the file transfer API call is made.
Rate limits
Admin SDK Directory API: 10 queries per second (QPS) per user; 1,500 queries per 100 seconds per project. Drive API: 1,000 requests per 100 seconds per user. At current volume, no throttling wrapper is needed. Implement retry with 2-second initial backoff on HTTP 429 or 403 rateLimitExceeded errors.
Constraints
File ownership transfer (Drive) can only be performed to users in the same Google Workspace domain. The transfer must be initiated before the account is suspended. Mailbox archiving (Vault hold) requires Google Vault, which is included in Business Plus and above; confirm the plan tier before build. Suspended accounts cannot send or receive email but their data is retained.
// POST https://admin.googleapis.com/admin/directory/v1/users (hire)
{
"primaryEmail": "{{workEmail}}",
"name": { "givenName": "{{firstName}}", "familyName": "{{lastName}}" },
"password": "{{generated_temp_password}}",
"changePasswordAtNextLogin": true,
"orgUnitPath": "{{resolved_ou_path}}"
}
// POST /admin/directory/v1/groups/{groupKey}/members (add to groups)
// POST https://www.googleapis.com/drive/v3/files/{fileId}/permissions (ownership transfer)
// POST /admin/directory/v1/users/{userKey} PATCH { "suspended": true } (offboarding)Integration and API SpecPage 2 of 4
FS-DOC-05Technical
Microsoft 365
Microsoft 365 integration handles licence assignment, Teams membership, and SharePoint site access on hire. On offboarding, the licence is removed, the user is removed from Teams groups, and the mailbox is placed on litigation hold or archived per the data retention policy. All Microsoft Graph API calls use the app-only Client Credentials flow.
Auth method
OAuth 2.0 Client Credentials flow via Azure AD app registration. Tenant ID stored as M365_TENANT_ID; Client ID as M365_CLIENT_ID; Client secret as M365_CLIENT_SECRET. Token endpoint: https://login.microsoftonline.com/{M365_TENANT_ID}/oauth2/v2.0/token.
Required scopes (application permissions)
User.ReadWrite.All, Group.ReadWrite.All, Directory.ReadWrite.All, Mail.ReadWrite (for archiving), Sites.Manage.All (for SharePoint). All are application-level permissions granted by a Global Admin in Azure AD > App registrations > API permissions > Grant admin consent.
Webhook / trigger
Not a trigger source. Action target only. Microsoft Graph change notifications (webhooks) are not used in this workflow.
Required configuration
Microsoft 365 licence SKU IDs for each role tier stored as config variables (e.g. SKU_BUSINESS_BASIC, SKU_BUSINESS_PREMIUM). Teams group object IDs per department stored as M365_GROUP_{DEPT}. SharePoint site IDs stored as M365_SHAREPOINT_{DEPT}. Usage location must be set per user (e.g. US) before a licence can be assigned.
Rate limits
Microsoft Graph enforces throttling per-app and per-tenant. Standard limit is approximately 10,000 requests per 10 minutes per app. On hire or offboarding, expected call count is 8 to 15 requests per event. No throttling logic required at current volume. Retry on HTTP 429 using the Retry-After header value.
Constraints
Licence assignment requires usageLocation to be set first; the workflow must patch the user record before assigning a SKU. Removing a licence does not delete the mailbox; a litigation hold or archive must be set separately. Azure AD app permissions require explicit admin consent and cannot be self-consented.
// POST https://graph.microsoft.com/v1.0/users (hire)
{
"displayName": "{{firstName}} {{lastName}}",
"mailNickname": "{{mailNickname}}",
"userPrincipalName": "{{workEmail}}",
"usageLocation": "US",
"accountEnabled": true,
"passwordProfile": { "password": "{{generated_temp_password}}", "forceChangePasswordNextSignIn": true }
}
// POST /v1.0/users/{userId}/assignLicense (SKU assignment)
// POST /v1.0/groups/{groupId}/members/$ref (Teams/SharePoint)
// PATCH /v1.0/users/{userId} { "accountEnabled": false } (offboarding)Slack
Slack is used for two purposes: operational actions (workspace invite, channel membership, deactivation on offboarding) and notifications (manager alerts on completion). The Slack bot token must have both user management and messaging scopes. Full deactivation via the SCIM API requires Slack Business+.
Auth method
OAuth 2.0 bot token obtained through Slack app installation. Bot token stored as SLACK_BOT_TOKEN. For SCIM-based deactivation, a separate SCIM API token is stored as SLACK_SCIM_TOKEN. App installed to the workspace by a Slack admin.
Required scopes (bot token)
users:write, users:read, users:read.email, channels:manage, groups:write, im:write, mpim:write, chat:write, chat:write.public. SCIM scope is granted separately at the workspace level and does not use OAuth scopes.
Webhook / trigger
Not a trigger source. Slack is an action target and a notification channel. No inbound Slack events are consumed by this workflow.
Required configuration
Slack workspace ID stored as SLACK_WORKSPACE_ID. Channel IDs per role stored in the workflow config as SLACK_CHANNEL_{ROLE} (e.g. SLACK_CHANNEL_ENGINEERING, SLACK_CHANNEL_SALES). Manager notification channel or DM target resolved from the BambooHR supervisorId and matched to SLACK_MANAGER_EMAIL_MAP in the config. SCIM base URL: https://api.slack.com/scim/v2/.
Rate limits
Slack Web API: Tier 2 methods (users.invite, conversations.invite) are limited to 20 requests/minute. Tier 1 (chat.postMessage) is 1 request/minute per channel. At current event volumes, no throttling required. Add a 1-second delay between successive channel membership calls to avoid burst limits.
Constraints
Slack does not allow direct account creation via API; users must be invited by email. The invited user must accept before they appear as Active. For offboarding, deactivation via SCIM sets the user to inactive immediately without requiring acceptance. Deactivated users cannot be searched via users.lookupByEmail until reactivated.
// POST https://slack.com/api/users.admin.invite (hire - requires admin token)
{ "email": "{{workEmail}}", "channels": "{{channel_ids_csv}}", "real_name": "{{fullName}}" }
// PATCH https://api.slack.com/scim/v2/Users/{userId} (offboarding)
{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "active": false }
// POST https://slack.com/api/chat.postMessage (manager notification)
{
"channel": "{{manager_slack_id}}",
"text": "Provisioning complete for {{fullName}}. All accounts actioned. Audit ticket: {{jira_ticket_url}}"
}Jira
Jira is used exclusively by Agent 2 (Offboarding Audit Agent) to create timestamped audit tickets for every termination event. A separate issue type is used for elevated-permissions review requests, which Agent 1 raises when a non-standard access profile is detected. All calls use the Jira Cloud REST API v3.
Auth method
HTTP Basic Auth using the service account email and an API token. Credentials stored as JIRA_EMAIL and JIRA_API_TOKEN. Base URL stored as JIRA_BASE_URL (e.g. https://yourcompany.atlassian.net).
Required scopes
Jira API tokens do not use OAuth scopes. The service account user must have the following Jira project permissions in the target project: Create Issues, Edit Issues, Add Comments, Transition Issues. These are granted via the project permission scheme in Jira admin settings.
Webhook / trigger
Not a trigger source. Jira is an action target only. No inbound Jira webhooks are consumed by this workflow.
Required configuration
JIRA_PROJECT_KEY for the audit project stored in the credential store (e.g. ITAUDIT). JIRA_ISSUE_TYPE_AUDIT (e.g. IT Audit Record) and JIRA_ISSUE_TYPE_REVIEW (e.g. Access Review) stored as config variables. Custom field IDs for employee ID, deactivation timestamps, and systems actioned must be retrieved from the Jira field API and stored as JIRA_FIELD_EMPLOYEE_ID, JIRA_FIELD_SYSTEMS_ACTIONED, JIRA_FIELD_DEACTIVATION_TIMESTAMPS.
Rate limits
Jira Cloud enforces a rate limit of approximately 300 requests per minute per user. Audit ticket creation involves 2 to 4 API calls per event. No throttling required at current volume. Implement retry with 3-second backoff on HTTP 429.
Constraints
Custom field IDs in Jira Cloud are tenant-specific and cannot be hardcoded; they must be discovered via GET /rest/api/3/field and stored in config before build. Issue creation will fail silently if required fields are missing; validate the issue payload against the project field configuration during QA. Jira API does not support bulk issue creation; each audit ticket must be created individually.
// POST https://{JIRA_BASE_URL}/rest/api/3/issue (audit ticket creation)
{
"fields": {
"project": { "key": "{{JIRA_PROJECT_KEY}}" },
"summary": "Offboarding Audit: {{fullName}} - {{terminationDate}}",
"issuetype": { "name": "{{JIRA_ISSUE_TYPE_AUDIT}}" },
"{{JIRA_FIELD_EMPLOYEE_ID}}": "{{employeeId}}",
"{{JIRA_FIELD_SYSTEMS_ACTIONED}}": "Okta, Google Workspace, Microsoft 365, Slack",
"{{JIRA_FIELD_DEACTIVATION_TIMESTAMPS}}": "{{deactivation_log_json}}",
"description": {
"type": "doc", "version": 1,
"content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "{{audit_narrative}}" }] }]
}
}
}03Field mappings between tools
The tables below define the exact field mappings for each agent handoff point. Field names are shown in monospace format exactly as they appear in the source and destination API payloads. All mappings are resolved at runtime from the provisioning manifest produced by Agent 1.
Agent 1 handoff: BambooHR to Provisioning Manifest (internal)
Source tool
Source field
Destination tool
Destination field
BambooHR
`id`
Provisioning Manifest
`employee_id`
BambooHR
`firstName`
Provisioning Manifest
`first_name`
BambooHR
`lastName`
Provisioning Manifest
`last_name`
BambooHR
`workEmail`
Provisioning Manifest
`work_email`
BambooHR
`jobTitle`
Provisioning Manifest
`job_title`
BambooHR
`department`
Provisioning Manifest
`department`
BambooHR
`location`
Provisioning Manifest
`location`
BambooHR
`supervisorId`
Provisioning Manifest
`manager_id`
BambooHR
`hireDate`
Provisioning Manifest
`start_date`
BambooHR
`terminationDate`
Provisioning Manifest
`end_date`
BambooHR
`employmentStatus`
Provisioning Manifest
`event_type` (mapped: Active=hire, Terminated=offboard, Transferred=transfer)
BambooHR
`customField_access_tier`
Provisioning Manifest
`access_tier` (used to flag elevated permissions)
Agent 1 handoff: Provisioning Manifest to Okta
Source tool
Source field
Destination tool
Destination field
Provisioning Manifest
`first_name`
Okta
`profile.firstName`
Provisioning Manifest
`last_name`
Okta
`profile.lastName`
Provisioning Manifest
`work_email`
Okta
`profile.email`
Provisioning Manifest
`work_email`
Okta
`profile.login`
Provisioning Manifest
`department`
Okta
`profile.department`
Provisioning Manifest
`job_title`
Okta
`profile.title`
Provisioning Manifest
`resolved_group_ids`
Okta
`groupIds[]`
Provisioning Manifest
`employee_id`
Okta
`profile.employeeNumber`
Agent 1 handoff: Provisioning Manifest to Google Workspace
Source tool
Source field
Destination tool
Destination field
Provisioning Manifest
`first_name`
Google Workspace
`name.givenName`
Provisioning Manifest
`last_name`
Google Workspace
`name.familyName`
Provisioning Manifest
`work_email`
Google Workspace
`primaryEmail`
Provisioning Manifest
`resolved_ou_path`
Google Workspace
`orgUnitPath`
Provisioning Manifest
`google_group_ids[]`
Google Workspace
`members[].email` (via Groups API)
Provisioning Manifest
`generated_temp_password`
Google Workspace
`password`
Agent 1 handoff: Provisioning Manifest to Microsoft 365
Source tool
Source field
Destination tool
Destination field
Provisioning Manifest
`first_name` + `last_name`
Microsoft 365
`displayName`
Provisioning Manifest
`work_email`
Microsoft 365
`userPrincipalName`
Provisioning Manifest
`work_email` (prefix)
Microsoft 365
`mailNickname`
Provisioning Manifest
`resolved_sku_id`
Microsoft 365
`addLicenses[].skuId`
Provisioning Manifest
`m365_group_ids[]`
Microsoft 365
`groups/{groupId}/members/$ref`
Provisioning Manifest
`generated_temp_password`
Microsoft 365
`passwordProfile.password`
Agent 1 handoff: Provisioning Manifest to Slack
Source tool
Source field
Destination tool
Destination field
Provisioning Manifest
`work_email`
Slack
`email` (users.admin.invite)
Provisioning Manifest
`first_name` + `last_name`
Slack
`real_name`
Provisioning Manifest
`slack_channel_ids[]`
Slack
`channels` (csv of channel IDs)
Agent 2 handoff: Offboarding result log to Jira audit ticket
Source tool
Source field
Destination tool
Destination field
Provisioning Manifest
`employee_id`
Jira
`fields.{{JIRA_FIELD_EMPLOYEE_ID}}`
Provisioning Manifest
`first_name` + `last_name` + `end_date`
Jira
`fields.summary`
Agent 2 log
`okta_deactivated_at`
Jira
`fields.{{JIRA_FIELD_DEACTIVATION_TIMESTAMPS}}` (Okta entry)
Agent 2 log
`google_suspended_at`
Jira
`fields.{{JIRA_FIELD_DEACTIVATION_TIMESTAMPS}}` (Google entry)
Agent 2 log
`m365_disabled_at`
Jira
`fields.{{JIRA_FIELD_DEACTIVATION_TIMESTAMPS}}` (M365 entry)
Agent 2 log
`slack_deactivated_at`
Jira
`fields.{{JIRA_FIELD_DEACTIVATION_TIMESTAMPS}}` (Slack entry)
Agent 2 log
`systems_actioned[]`
Jira
`fields.{{JIRA_FIELD_SYSTEMS_ACTIONED}}`
Agent 2 log
`file_transfer_confirmed`
Jira
`fields.description` (audit narrative body)
Provisioning Manifest
`manager_id` (resolved email)
Jira
`fields.description` (manager reference)
Integration and API SpecPage 3 of 4
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Two discrete workflows, one per agent: Workflow 1 maps to the Provisioning Policy Agent (Agent 1); Workflow 2 maps to the Offboarding Audit Agent (Agent 2). Both workflows share a single credential store. Agent 2 is triggered by the output of Agent 1 when the event_type is offboard. For hire and transfer events, only Agent 1 executes.
Agent 1 trigger mechanism
Webhook (push). BambooHR posts to the automation platform inbound endpoint on every employee status change. The platform validates the HMAC-SHA256 signature in the X-BambooHR-Signature-V2 header before executing. No polling is used for the primary trigger. A supplementary daily poll of BambooHR (GET /employees?fields=employmentStatus) runs at 06:00 UTC to catch any missed webhooks.
Agent 2 trigger mechanism
Internal event (push from Agent 1 output). Agent 1 writes the deprovisioning manifest to a shared internal queue when event_type is offboard. Agent 2 reads from this queue immediately. No external webhook is used. Execution timeout for each deactivation step is 120 seconds; a failure alert fires if confirmation is not received within that window.
Elevated permissions gate
Agent 1 evaluates the access_tier field from BambooHR. If access_tier is elevated or non-standard, the workflow pauses, creates a Jira review issue (JIRA_ISSUE_TYPE_REVIEW), and sends a Slack DM to the IT administrator. Agent 1 resumes only after the Jira issue transitions to the Approved status, detected by a webhook from Jira or a poll every 5 minutes for up to 24 hours.
Credential store pattern
All secrets are stored in the automation platform credential store, never in workflow step definitions or config files. Variable names follow the convention shown in the code block below. Secrets are injected at runtime as environment references.
Deduplication
Each workflow execution is keyed on employee_id and event_type. If the same employee_id and event_type combination is received within a 60-second window, the second execution is suppressed and logged. This prevents duplicate provisioning from rapid successive BambooHR changes.
Audit logging (platform level)
Every workflow execution writes a structured execution log including: employee_id, event_type, trigger_timestamp, steps_completed[], steps_failed[], and final_status. These logs are retained for 90 days in the automation platform and are separate from the Jira audit ticket created by Agent 2.
Credential store contents (all secrets injected at runtime, never hardcoded)
# BambooHR
BAMBOOHR_API_KEY = <bamboohr_service_account_api_key>
BAMBOOHR_SUBDOMAIN = <your_bamboohr_subdomain>
BAMBOOHR_WEBHOOK_SECRET = <hmac_secret_from_bamboohr_webhook_config>
# Okta
OKTA_DOMAIN = <yourorg>.okta.com
OKTA_CLIENT_ID = <service_app_client_id>
OKTA_CLIENT_SECRET = <service_app_client_secret>
# Google Workspace
GOOGLE_SA_KEY_JSON = <service_account_json_key_contents>
GOOGLE_ADMIN_EMAIL = <admin_user_to_impersonate@yourdomain.com>
GOOGLE_PRIMARY_DOMAIN = <yourdomain.com>
# Microsoft 365
M365_TENANT_ID = <azure_ad_tenant_id>
M365_CLIENT_ID = <app_registration_client_id>
M365_CLIENT_SECRET = <app_registration_client_secret>
# Slack
SLACK_BOT_TOKEN = xoxb-<bot_token>
SLACK_SCIM_TOKEN = <scim_api_token>
SLACK_WORKSPACE_ID = T<workspace_id>
# Jira
JIRA_BASE_URL = https://<yourorg>.atlassian.net
JIRA_EMAIL = <service_account_email@yourdomain.com>
JIRA_API_TOKEN = <jira_api_token>
JIRA_PROJECT_KEY = ITAUDIT
JIRA_ISSUE_TYPE_AUDIT = IT Audit Record
JIRA_ISSUE_TYPE_REVIEW = Access Review
JIRA_FIELD_EMPLOYEE_ID = customfield_10042
JIRA_FIELD_SYSTEMS_ACTIONED = customfield_10043
JIRA_FIELD_DEACTIVATION_TIMESTAMPS = customfield_10044
# Workflow config (not secrets, but stored centrally)
OU_ENGINEERING = /corp/engineering
OU_SALES = /corp/sales
OU_OPERATIONS = /corp/operations
GROUP_ID_ENGINEERING = 00g1abc123def456
GROUP_ID_SALES = 00g2abc123def456
SKU_BUSINESS_BASIC = 3b555118-157b-4e84-9b7a-cbb6e7a18972
SKU_BUSINESS_PREMIUM = cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46
SLACK_CHANNEL_ENGINEERING = C01ABCDEF12
SLACK_CHANNEL_SALES = C01BCDEF123
The GOOGLE_SA_KEY_JSON credential contains the full private key for the service account. It must be stored as a single-line JSON string or a secret reference. It must never appear in a workflow step log, a comment, or a config file committed to version control.
05Error handling and retry logic
Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently. If a step does not produce a confirmed success response, the workflow must log the failure, alert the IT administrator via Slack DM, and halt further downstream steps until the failure is resolved or manually overridden.
Integration
Scenario
Required behaviour
BambooHR webhook
Webhook payload signature validation fails
Reject the payload immediately with HTTP 400. Log the raw payload and timestamp. Send a Slack alert to the IT administrator. Do not execute any provisioning steps. Investigate for replay attack or misconfigured secret.
BambooHR webhook
Webhook received but follow-up GET /employees/{id} returns 404 or 500
Retry GET up to 3 times with exponential backoff (5s, 15s, 45s). If all retries fail, halt the workflow, log the employee_id and error response, and send a Slack alert. Do not proceed to provisioning without the full employee record.
BambooHR daily poll
Polling request returns 429 (rate limit)
Respect the Retry-After header. Delay the poll and retry once. If still rate-limited, skip the poll cycle and log a warning. The missed poll is non-critical given the primary webhook trigger.
Okta
User creation returns 409 (user already exists)
Log the conflict, fetch the existing Okta user by email, compare profile fields, and update only if department or title has changed (PUT /users/{id}). Do not create a duplicate. Alert the IT administrator with the existing user ID for review.
Okta
Group membership assignment fails (403 or 404)
Retry up to 3 times with 10-second backoff. If the group ID is not found (404), halt provisioning, alert IT administrator with the failed group ID, and flag the provisioning manifest as incomplete. Do not proceed to Google Workspace until Okta group membership is confirmed.
Okta
Deactivation call fails during offboarding
This is a critical security failure. Retry suspend immediately up to 5 times (2s, 5s, 10s, 20s, 30s). If all retries fail, send an urgent Slack DM and email to the IT administrator and halt the offboarding workflow. Log the failure in the Jira audit ticket with a FAILED status. The IT administrator must manually deactivate and confirm before the workflow can be marked complete.
Google Workspace
User creation fails (403 domain policy or quota)
Retry up to 3 times with 15-second backoff. If failure persists, log the error code and description, alert IT administrator, and mark the Google Workspace step as FAILED in the provisioning manifest. Continue to Microsoft 365 and Slack steps but flag the manifest as incomplete.
Google Workspace
File ownership transfer fails during offboarding (manager not found or different domain)
Log the failure with the target manager email. Attempt to fall back to the next-level manager by re-querying BambooHR for the supervisorId of the original supervisorId. If still unresolved, pause and alert IT administrator for manual ownership assignment. Do not suspend the Google account until ownership transfer is confirmed or manually overridden.
Microsoft 365
Licence assignment fails (no available licences in pool)
Do not retry. Log the SKU ID and tenant licence count. Send an immediate alert to the IT administrator with the licence pool shortfall. Create a Jira issue for licence procurement. The user account is created without a licence; the licence assignment step must be re-triggered manually once additional licences are available.
Slack
User invite fails (email already in workspace or deactivated account)
Attempt to look up the user by email via users.lookupByEmail. If a deactivated account is found, call users.admin.setActive to reactivate it. If an active account is found, proceed directly to channel membership. If the API returns a fatal error, log and alert IT administrator. Do not retry more than 2 times.
Slack
SCIM deactivation fails during offboarding
This is a security-relevant failure. Retry immediately up to 3 times (3s, 10s, 30s). If all retries fail, send an urgent Slack alert to IT administrator and log FAILED in the Jira audit ticket. The IT administrator must manually deactivate the account in the Slack admin console and confirm. The Jira ticket must not be marked closed until deactivation is confirmed.
Jira
Audit ticket creation fails (field validation error or project permission denied)
Retry up to 3 times with 5-second backoff. If all retries fail, write the full audit payload to the automation platform execution log as a fallback audit record. Send a Slack alert to IT administrator with the raw audit data. The execution log entry must be treated as the authoritative audit record until the Jira ticket is created manually. Never discard the audit payload.
Any failure in the Okta or Slack deactivation steps during an offboarding event must be treated as an active security incident until the account is confirmed inactive. The IT administrator must acknowledge the alert within 30 minutes. The Jira audit ticket must remain in OPEN status until every deactivation step is confirmed complete.
For questions about this specification, contact the FullSpec build team at support@gofullspec.com.
Integration and API SpecPage 4 of 4