FS-DOC-05Technical
Integration and API Spec
Device and Hardware Management
[YourCompany.com] · IT Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for every integration point in the Device and Hardware Management automation. It covers tool inventory, exact OAuth scopes, webhook configurations, field mappings between agents, orchestration layout, credential store contents, and defined error-handling behaviour at every integration boundary. The FullSpec team uses this specification to build and wire the three agents: Asset Register Agent, Device Lifecycle Comms Agent, and MDM Sync Agent. No integration detail should be hardcoded outside the credential store defined in section 04.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Airtable
Central device asset register: availability queries, record writes, status flags
OAuth 2.0 / Personal Access Token
Team ($20/seat/month)
Agent 1, Agent 3
Jira
IT service ticket creation, update, and status sync
OAuth 2.0 (3-legged)
Standard ($7.75/user/month)
Agent 1
Jamf Pro
MDM enrollment verification, policy profile assignment, remote wipe trigger
Bearer token (API role)
Jamf Pro (cloud or self-hosted)
Agent 3
Gmail
Assignment confirmations, audit request emails, overdue return reminders
OAuth 2.0 (Google Workspace)
Google Workspace Business Starter
Agent 2
Slack
IT channel alerts, procurement prompts, manager notifications
OAuth 2.0 (Slack app)
Pro ($7.25/user/month) or Free tier
Agent 2
Google Sheets
Fallback HR data sync source for hire/departure events when webhook unavailable
OAuth 2.0 (Google Workspace)
Google Workspace Business Starter
Agent 1 (fallback)
Orchestration layer
Workflow automation platform hosting all three agent workflows, credential store, and retry logic
Platform-managed service account
Vendor-dependent (platform licence)
All agents
Before you connect anything: provision sandbox or development credentials for every tool listed above and validate each connection end-to-end in a non-production environment before any production credential is entered into the credential store. Jamf sandbox testing is especially critical: a misconfigured wipe call in production cannot be undone.
02Per-tool integration detail
Airtable
Serves as the live device asset register. The orchestration layer reads and writes records in the Devices base via the Airtable REST API v0. All three agents interact with Airtable: Agent 1 queries and writes assignment and return records; Agent 3 writes MDM status back after Jamf calls.
Auth method
Personal Access Token (PAT) scoped to the specific base, stored in the credential store as AIRTABLE_PAT. OAuth 2.0 is supported for user-facing flows but PAT is preferred for server-side automation.
Required scopes
data.records:read data.records:write schema.bases:read
Webhook/trigger setup
Airtable Automations webhook is not used. The orchestration layer polls the Devices table every 5 minutes for status changes using a filterByFormula query. Airtable's native webhook (via the Airtable API /webhooks endpoint) may be enabled for record-create events to reduce polling latency: POST to https://api.airtable.com/v0/bases/{baseId}/webhooks with cursor tracking enabled.
Required configuration
Base ID stored as AIRTABLE_BASE_ID in the credential store. Table name: Devices. Required fields: device_serial (text), assigned_to (linked record to Staff table), assignment_date (date), expected_return_date (date), status (single select: Available/Assigned/In Wipe/Quarantine/EOL), jamf_enrollment_status (single select: Enrolled/Not Enrolled/Wipe Pending/Wipe Confirmed), jira_ticket_id (text), last_updated (date/time, auto). Template view name: Automation_View (filtered to exclude EOL records).
Rate limits
5 requests/second per base. At ~40 device events/month plus 5-minute polling, peak sustained rate is well under 1 req/sec. Throttling is not required at current volume; implement a 250ms inter-request delay as a precaution.
Constraints
Linked record fields require the record ID of the linked Staff record, not the display name. Staff table must be pre-populated. Attachment fields are not used in this automation. Formula fields are read-only via the API.
// Query: find available device by spec
GET /v0/{AIRTABLE_BASE_ID}/Devices
?filterByFormula=AND({status}='Available',{device_type}='{requested_type}')
&maxRecords=1
// Write: assignment record update
PATCH /v0/{AIRTABLE_BASE_ID}/Devices/{recordId}
body: { fields: { assigned_to, assignment_date, expected_return_date, status, jira_ticket_id } }
// Output: updated record object with all field valuesJira
Used by Agent 1 (Asset Register Agent) to create and update IT service tickets for every device event, ensuring an audit trail independent of the Airtable register. Jira is also the primary inbound trigger for device request events.
Auth method
OAuth 2.0 (3-legged), using the Atlassian OAuth 2.0 app registered in the Atlassian developer console. Client ID and secret stored as JIRA_CLIENT_ID and JIRA_CLIENT_SECRET. Access token refreshed automatically; refresh token stored as JIRA_REFRESH_TOKEN.
Required scopes
read:jira-work write:jira-work read:jira-user offline_access
Webhook/trigger setup
A Jira webhook is registered via the Jira REST API (POST /rest/webhooks/1.0/webhook) to fire on issue_created and issue_updated events filtered to project key: IT and issue type: Service Request. Webhook URL points to the orchestration layer's inbound endpoint. Jira signs payloads with a shared secret (JIRA_WEBHOOK_SECRET); the orchestration layer validates the X-Hub-Signature header (HMAC-SHA256) before processing.
Required configuration
JIRA_BASE_URL, JIRA_PROJECT_KEY (IT), JIRA_ISSUE_TYPE_ID (Service Request), and JIRA_DEVICE_FIELD_ID (custom field for device serial) all stored in the credential store. Pipeline status values used: Open, In Progress, Awaiting Device, Assigned, Closed. Do not hardcode status IDs; resolve them via GET /rest/api/3/status at build time.
Rate limits
Jira Cloud REST API: 10,000 requests/day for Standard plan. At 40 events/month the automation uses fewer than 200 API calls/month. No throttling needed.
Constraints
Jira transition IDs for status changes are instance-specific and must be queried (GET /rest/api/3/issue/{issueId}/transitions) rather than hardcoded. Custom field IDs for device serial must be retrieved from the field schema at build time and stored in config.
// Inbound webhook payload (trigger)
POST {orchestration_inbound_url}/jira-webhook
headers: { X-Hub-Signature: 'sha256={hmac}' }
body: { issue: { id, key, fields: { summary, status, assignee, customfield_device_serial } } }
// Create ticket
POST /rest/api/3/issue
body: { fields: { project: {key}, issuetype: {id}, summary, description, customfield_device_serial } }
// Transition ticket status
POST /rest/api/3/issue/{issueId}/transitions
body: { transition: { id: '{JIRA_TRANSITION_ID_ASSIGNED}' } }Integration and API SpecPage 1 of 4
FS-DOC-05Technical
Jamf Pro
Used exclusively by Agent 3 (MDM Sync Agent) to verify device enrollment, apply policy profiles on assignment, and trigger remote wipe on returned devices. Jamf is the highest-risk integration: wipe calls are irreversible and must be gated by a confirmation check against the Airtable record before execution.
Auth method
Jamf Pro API uses Bearer token authentication. Token obtained via POST /api/v1/auth/token with Basic Auth (JAMF_API_USER, JAMF_API_PASSWORD). Token expires after 30 minutes; the orchestration layer refreshes it proactively every 25 minutes using POST /api/v1/auth/keep-alive. Credentials stored as JAMF_BASE_URL, JAMF_API_USER, JAMF_API_PASSWORD.
Required scopes
Jamf API Role permissions required: Read Computers Update Computers Send Computer Remote Wipe Command Read Mobile Devices Update Mobile Devices Send Mobile Device Remote Wipe Command Read Computer Management Assign Computer Policies
Webhook/trigger setup
No inbound Jamf webhook is used. Agent 3 is triggered by an Airtable record-write event from Agent 1. Jamf may optionally emit enrollment webhooks (configured via Jamf Pro Settings > Global > Webhooks) to a secondary endpoint for enrollment confirmation callbacks, but this is optional at current volume.
Required configuration
JAMF_BASE_URL (e.g. https://yourcompany.jamfcloud.com), JAMF_POLICY_PROFILE_ID_STANDARD (the profile ID applied on assignment), JAMF_CATEGORY_ASSIGNED, JAMF_CATEGORY_AVAILABLE all stored in credential store. Self-hosted Jamf Pro instances require the orchestration platform's egress IP ranges to be allowlisted in the Jamf network access rules.
Rate limits
Jamf Pro Cloud imposes no published hard rate limit for the Classic and Pro APIs but recommends no more than 20 concurrent requests. At 40 events/month, peak usage is negligible. A 500ms delay between sequential Jamf API calls is sufficient.
Constraints
Remote wipe via POST /api/v1/computers-preview/{id}/erase is irreversible. The orchestration layer must confirm: (1) Airtable status field equals 'In Wipe' and (2) Airtable assigned_to field has been cleared, before issuing the wipe command. Wipe confirmation is polled (GET /api/v1/computers-preview/{id}) until management_status returns 'Erased' before writing 'Wipe Confirmed' to Airtable. Physical device handover confirmation remains a manual human step and must not be bypassed.
// Get Bearer token
POST {JAMF_BASE_URL}/api/v1/auth/token
headers: { Authorization: 'Basic {base64(JAMF_API_USER:JAMF_API_PASSWORD)}' }
response: { token, expires }
// Verify enrollment
GET {JAMF_BASE_URL}/api/v1/computers-preview?filter=serialNumber=={device_serial}
headers: { Authorization: 'Bearer {token}' }
response: { results: [{ id, serialNumber, managementStatus, policyIds }] }
// Trigger remote wipe (IRREVERSIBLE - confirm Airtable status before calling)
POST {JAMF_BASE_URL}/api/v1/computers-preview/{computerId}/erase
body: { pin: '{JAMF_WIPE_PIN}' }
// Poll wipe status
GET {JAMF_BASE_URL}/api/v1/computers-preview/{computerId}
until managementStatus == 'Erased'Gmail
Used by Agent 2 (Device Lifecycle Comms Agent) to send all outbound emails: assignment confirmations, quarterly audit requests, overdue return reminders, and procurement alerts. All emails use stored templates with dynamic field substitution from Airtable record data.
Auth method
OAuth 2.0 via Google Workspace. Service account with domain-wide delegation enabled, impersonating the IT coordinator's address. Credentials stored as GMAIL_SERVICE_ACCOUNT_KEY_JSON and GMAIL_SENDER_ADDRESS.
Required scopes
https://www.googleapis.com/auth/gmail.send https://www.googleapis.com/auth/gmail.readonly https://www.googleapis.com/auth/gmail.labels
Webhook/trigger setup
Gmail push notifications (Google Cloud Pub/Sub) are configured for the IT inbox to detect inbound audit response emails. Pub/Sub topic stored as GMAIL_PUBSUB_TOPIC. The orchestration layer subscribes to the topic and filters messages by thread ID to correlate responses with the originating audit request. HMAC validation is not available natively; message authenticity is verified by checking the Pub/Sub subscription source.
Required configuration
Email template IDs are not native Gmail objects; templates are stored as plain-text or HTML strings in the credential/config store under keys: TEMPLATE_ASSIGNMENT_CONFIRMATION, TEMPLATE_AUDIT_REQUEST, TEMPLATE_OVERDUE_REMINDER, TEMPLATE_PROCUREMENT_ALERT. Placeholder tokens use the format {{field_name}} and are substituted at send time from the Airtable record payload. The IT coordinator's Gmail label 'DeviceAutomation' (label ID stored as GMAIL_LABEL_DEVICE_AUTOMATION) is applied to all sent and received automation-related threads.
Rate limits
Gmail API: 250 quota units/second per user; sending limit 2,000 messages/day for Google Workspace accounts. At 40 events/month peak sending is under 10 messages/day. No throttling required.
Constraints
Domain-wide delegation must be granted in the Google Workspace Admin console. The service account's client ID must be added to the Admin SDK authorised clients list with the scopes above. Gmail API does not support scheduling; time-delayed reminders are handled by the orchestration layer's wait/delay node, not by Gmail itself.
// Send email via Gmail API
POST https://gmail.googleapis.com/gmail/v1/users/{GMAIL_SENDER_ADDRESS}/messages/send
headers: { Authorization: 'Bearer {google_access_token}' }
body: { raw: '{base64url_encoded_RFC2822_message}' }
// Template substitution (orchestration layer, before API call)
template = TEMPLATE_ASSIGNMENT_CONFIRMATION
.replace('{{employee_name}}', airtable_record.assigned_to_name)
.replace('{{device_serial}}', airtable_record.device_serial)
.replace('{{device_type}}', airtable_record.device_type)
.replace('{{expected_return_date}}', airtable_record.expected_return_date)
// Inbound: Pub/Sub push to orchestration endpoint
POST {orchestration_inbound_url}/gmail-pubsub
body: { message: { data: base64(gmail_notification), messageId, publishTime } }Slack
Used by Agent 2 (Device Lifecycle Comms Agent) to post alerts to the IT channel and direct messages to the IT manager. Covers procurement prompts when no device is available and post-assignment summaries.
Auth method
OAuth 2.0 Slack app installation. Bot token stored as SLACK_BOT_TOKEN. The Slack app requires the bot to be invited to the target channel before posting is possible.
Required scopes
chat:write chat:write.public channels:read users:read users:read.email im:write
Webhook/trigger setup
No inbound Slack events are consumed by this automation at current scope. Outbound only: chat.postMessage API calls. If future scope requires Slack approval workflows, Slack interactive components (Block Kit actions) would be added with request URL validation using SLACK_SIGNING_SECRET.
Required configuration
SLACK_BOT_TOKEN, SLACK_IT_CHANNEL_ID (the #it-ops channel ID, not name), SLACK_IT_MANAGER_USER_ID all stored in credential store. Block Kit message templates for procurement alert and assignment summary stored as JSON strings under TEMPLATE_SLACK_PROCUREMENT and TEMPLATE_SLACK_ASSIGNMENT_SUMMARY. Channel and user IDs must be resolved at build time via conversations.list and users.lookupByEmail and stored by ID, not by display name.
Rate limits
Slack Web API Tier 3: 50+ requests/minute for chat.postMessage. At 40 events/month peak usage is approximately 80-120 Slack messages/month, well within limits. No throttling needed.
Constraints
Slack free plan retains only 90 days of message history. Pro plan or above is required if audit trail of Slack notifications is needed beyond 90 days. Bot token must be rotated if the Slack app is reinstalled. SLACK_BOT_TOKEN must be refreshed in the credential store after any Slack app reinstallation.
// Post to IT channel
POST https://slack.com/api/chat.postMessage
headers: { Authorization: 'Bearer {SLACK_BOT_TOKEN}' }
body: {
channel: '{SLACK_IT_CHANNEL_ID}',
blocks: [{TEMPLATE_SLACK_ASSIGNMENT_SUMMARY substituted with event data}]
}
// DM to IT manager (procurement alert)
POST https://slack.com/api/chat.postMessage
body: {
channel: '{SLACK_IT_MANAGER_USER_ID}',
blocks: [{TEMPLATE_SLACK_PROCUREMENT substituted with requested_device_type, requestor_name}]
}Google Sheets
Used as a fallback HR data sync source by Agent 1 when no native HR webhook is available. A scheduled daily poll reads a shared sheet maintained by the HR or operations team for new hire and departure events not yet captured in Jira.
Auth method
OAuth 2.0 service account with the Sheets API enabled in the Google Cloud project. Same service account as Gmail where possible. Credentials stored as GSHEETS_SERVICE_ACCOUNT_KEY_JSON and GSHEETS_SPREADSHEET_ID.
Required scopes
https://www.googleapis.com/auth/spreadsheets.readonly
Webhook/trigger setup
No native webhook. The orchestration layer runs a scheduled poll at 08:00 local time daily, reading rows added since the last processed row (tracked by a cursor column: processed_by_automation, boolean). Only rows where processed_by_automation is FALSE are actioned.
Required configuration
GSHEETS_SPREADSHEET_ID, GSHEETS_SHEET_NAME (e.g. HR_Events), GSHEETS_HEADER_ROW_INDEX (1). Expected columns: event_type (text: hire/departure/request), employee_name, employee_email, start_date, device_type_requested, processed_by_automation (boolean). Column order must match exactly; any schema change requires a config update.
Rate limits
Sheets API v4: 300 requests/minute per project. Daily poll uses 1-2 requests. No throttling needed.
Constraints
Google Sheets is a fallback path only. If a Jira webhook is functional, the Sheets poll is disabled to avoid duplicate event processing. The orchestration layer includes a deduplication check using employee_email plus event_type plus start_date as a composite key before creating any Airtable record.
// Read new HR events
GET https://sheets.googleapis.com/v4/spreadsheets/{GSHEETS_SPREADSHEET_ID}/values/{GSHEETS_SHEET_NAME}
headers: { Authorization: 'Bearer {google_access_token}' }
// Filter unprocessed rows in orchestration layer
rows.filter(row => row.processed_by_automation === 'FALSE')
// Output per row: { event_type, employee_name, employee_email, start_date, device_type_requested }Integration and API SpecPage 2 of 4
FS-DOC-05Technical
03Field mappings between tools
The following tables define every field handoff between tools at each agent boundary. All field names are exact as they appear in the respective APIs. Orchestration layer mapping logic must use these names verbatim.
Agent 1 handoff: Jira (or Google Sheets) to Airtable
Source tool
Source field
Destination tool
Destination field
Jira
`issue.key`
Airtable
`jira_ticket_id`
Jira
`issue.fields.summary`
Airtable
`event_summary`
Jira
`issue.fields.assignee.emailAddress`
Airtable
`requestor_email`
Jira
`issue.fields.customfield_device_serial`
Airtable
`device_serial`
Jira
`issue.fields.status.name`
Airtable
`jira_status`
Google Sheets
`employee_email`
Airtable
`requestor_email`
Google Sheets
`employee_name`
Airtable
`assigned_to_name`
Google Sheets
`device_type_requested`
Airtable
`device_type`
Google Sheets
`start_date`
Airtable
`assignment_date`
Google Sheets
`event_type`
Airtable
`event_type`
Agent 1 to Agent 3 handoff: Airtable record written by Asset Register Agent consumed by MDM Sync Agent
Source tool
Source field
Destination tool
Destination field
Airtable
`device_serial`
Jamf Pro
`serialNumber` (query param in GET /computers-preview)
Airtable
`status`
Jamf Pro
Gate condition: must equal `'Assigned'` before enrollment call
Airtable
`status`
Jamf Pro
Gate condition: must equal `'In Wipe'` before wipe call
Airtable
`jamf_enrollment_status`
Airtable
`jamf_enrollment_status` (written back after Jamf response)
Jamf Pro
`managementStatus`
Airtable
`jamf_enrollment_status`
Jamf Pro
`id` (computer record ID)
Airtable
`jamf_computer_id` (stored for subsequent calls)
Agent 1 to Agent 2 handoff: Airtable record consumed by Device Lifecycle Comms Agent for Gmail and Slack
Source tool
Source field
Destination tool
Destination field
Airtable
`assigned_to_name`
Gmail
`{{employee_name}}` in email template
Airtable
`requestor_email`
Gmail
`To` header
Airtable
`device_serial`
Gmail
`{{device_serial}}` in email template
Airtable
`device_type`
Gmail
`{{device_type}}` in email template
Airtable
`expected_return_date`
Gmail
`{{expected_return_date}}` in email template
Airtable
`device_type`
Slack
`{{requested_device_type}}` in Block Kit template
Airtable
`assigned_to_name`
Slack
`{{requestor_name}}` in Block Kit template
Airtable
`jira_ticket_id`
Slack
`{{jira_ticket_id}}` in Block Kit template
Airtable
`device_serial`
Slack
`{{device_serial}}` in Block Kit template
Airtable
`status`
Airtable
`comms_sent` (boolean flag written after email/Slack send confirmed)
Agent 1 Jira write-back: Airtable to Jira after record update
Source tool
Source field
Destination tool
Destination field
Airtable
`jira_ticket_id`
Jira
`issueId` (path param in transition/update call)
Airtable
`device_serial`
Jira
`customfield_device_serial`
Airtable
`status`
Jira
`transition.id` (mapped: Assigned -> JIRA_TRANSITION_ID_ASSIGNED)
Airtable
`assignment_date`
Jira
`fields.description` (appended as audit note)
04Build stack and orchestration
Orchestration layout
Three discrete workflows, one per agent, running in the same automation platform workspace. Each workflow is independently deployable and versioned. A shared credential store is referenced by all three workflows using named credential identifiers (no inline secrets in any workflow node).
Agent 1 trigger: Asset Register Agent
Webhook trigger (Jira webhook, event: issue_created / issue_updated). Payload validated using HMAC-SHA256 against JIRA_WEBHOOK_SECRET before any downstream node executes. Fallback: scheduled poll of Google Sheets every day at 08:00 local time, active only when JIRA_WEBHOOK_ENABLED flag is set to false in config.
Agent 2 trigger: Device Lifecycle Comms Agent
Event-driven trigger listening for a record-update event on the Airtable Devices table (polled every 5 minutes or via Airtable webhook cursor). Fires when the status field transitions to 'Assigned', 'In Wipe', or 'Quarantine', or when a scheduled audit date is reached (cron: 0 8 1 */3 * for quarterly at 08:00 on the first of the month).
Agent 3 trigger: MDM Sync Agent
Event-driven trigger on Airtable record-write completion from Agent 1. Specifically listens for jamf_enrollment_status set to 'Pending Enrollment' or 'Wipe Pending'. No Jamf-initiated webhook is consumed unless the optional enrollment callback endpoint is enabled.
Signature validation
Jira webhook: X-Hub-Signature-256 header, HMAC-SHA256 of raw request body using JIRA_WEBHOOK_SECRET. Validation failure returns HTTP 401 and logs the raw header for debugging. Gmail Pub/Sub: authenticity verified by checking the subscription push endpoint URL matches the registered subscription. Slack (future): X-Slack-Signature, HMAC-SHA256 using SLACK_SIGNING_SECRET.
Deduplication
Composite key (employee_email + event_type + event_date) checked against a 'processed_events' log table in Airtable before any record write. Duplicate keys are skipped and logged with status 'Duplicate Skipped'.
Credential store
All secrets stored in the orchestration platform's encrypted credential store. No credential is written into any workflow node configuration field, environment variable in plaintext, or source-controlled file. See code block below for the full credential store inventory.
Full credential store inventory. No entry in this list may be hardcoded in any workflow node, script, or repository file.
// Credential store inventory — Device and Hardware Management automation
// All entries stored in the orchestration platform encrypted credential store
// Airtable
AIRTABLE_PAT // Personal Access Token, scoped to Devices base
AIRTABLE_BASE_ID // e.g. appXXXXXXXXXXXXXX
// Jira
JIRA_CLIENT_ID // Atlassian OAuth 2.0 app client ID
JIRA_CLIENT_SECRET // Atlassian OAuth 2.0 app client secret
JIRA_REFRESH_TOKEN // Long-lived refresh token (rotated on expiry)
JIRA_BASE_URL // e.g. https://yourcompany.atlassian.net
JIRA_PROJECT_KEY // IT
JIRA_ISSUE_TYPE_ID // Resolved at build time, stored as string
JIRA_TRANSITION_ID_ASSIGNED // Resolved at build time, stored as string
JIRA_TRANSITION_ID_CLOSED // Resolved at build time, stored as string
JIRA_WEBHOOK_SECRET // Shared secret for HMAC-SHA256 validation
JIRA_WEBHOOK_ENABLED // Boolean flag: true/false
// Jamf Pro
JAMF_BASE_URL // e.g. https://yourcompany.jamfcloud.com
JAMF_API_USER // API role username (not admin account)
JAMF_API_PASSWORD // API role password
JAMF_POLICY_PROFILE_ID_STANDARD // Policy profile ID applied on assignment
JAMF_CATEGORY_ASSIGNED // Jamf category string for assigned devices
JAMF_CATEGORY_AVAILABLE // Jamf category string for available devices
JAMF_WIPE_PIN // 6-digit PIN required for erase API call
// Gmail
GMAIL_SERVICE_ACCOUNT_KEY_JSON // Google service account key (JSON, base64-encoded)
GMAIL_SENDER_ADDRESS // e.g. it@yourcompany.com
GMAIL_PUBSUB_TOPIC // Google Cloud Pub/Sub topic name
GMAIL_LABEL_DEVICE_AUTOMATION // Gmail label ID (not display name)
// Email templates (stored as config, not secrets, but in credential store for consistency)
TEMPLATE_ASSIGNMENT_CONFIRMATION // HTML string with {{field_name}} placeholders
TEMPLATE_AUDIT_REQUEST // HTML string with {{field_name}} placeholders
TEMPLATE_OVERDUE_REMINDER // HTML string with {{field_name}} placeholders
TEMPLATE_PROCUREMENT_ALERT // HTML string with {{field_name}} placeholders
// Slack
SLACK_BOT_TOKEN // xoxb- prefixed bot token
SLACK_IT_CHANNEL_ID // Channel ID (not display name)
SLACK_IT_MANAGER_USER_ID // User ID (not display name)
SLACK_SIGNING_SECRET // For future interactive component validation
TEMPLATE_SLACK_PROCUREMENT // Block Kit JSON string
TEMPLATE_SLACK_ASSIGNMENT_SUMMARY // Block Kit JSON string
// Google Sheets (fallback)
GSHEETS_SERVICE_ACCOUNT_KEY_JSON // Reuse Gmail service account where possible
GSHEETS_SPREADSHEET_ID // Spreadsheet ID from URL
GSHEETS_SHEET_NAME // e.g. HR_EventsIntegration and API SpecPage 3 of 4
FS-DOC-05Technical
05Error handling and retry logic
Unhandled exceptions must never fail silently. Every integration point has a defined failure behaviour. Any error not caught by the rows below must be routed to the generic error handler: log the full error payload to the Airtable error_log table, post a Slack alert to SLACK_IT_CHANNEL_ID with the error summary and the affected record ID, and halt the current workflow branch without retrying automatically.
Integration
Scenario
Required behaviour
Jira webhook
HMAC-SHA256 signature validation fails on inbound payload
Return HTTP 401. Log raw X-Hub-Signature header and request timestamp to Airtable error_log. Do not process payload. Post Slack alert to IT channel. No retry: signature failure indicates a configuration or security issue requiring manual review.
Jira REST API
HTTP 429 rate limit response on ticket create or transition call
Retry with exponential backoff: wait 2s, 4s, 8s (max 3 retries). If still failing after 3 retries, log to error_log with jira_ticket_id and post Slack alert. Mark Airtable record with status 'Jira Sync Pending' for manual follow-up.
Jira REST API
HTTP 401 or 403: OAuth token expired or insufficient scope
Attempt token refresh using JIRA_REFRESH_TOKEN. If refresh succeeds, retry the original call once. If refresh fails, log credential error to error_log, alert IT channel via Slack, and halt workflow. Do not retry indefinitely. Token rotation requires manual credential store update.
Airtable REST API
HTTP 422 Unprocessable Entity on record write (e.g. invalid linked record ID)
Log full error response including field name and value to error_log. Post Slack alert with affected record details. Mark Airtable record status as 'Write Error'. Do not retry automatically: the error indicates a data quality issue (mismatched Staff record ID) requiring human correction before reprocessing.
Airtable REST API
HTTP 429 rate limit (exceeds 5 req/sec)
Implement 250ms inter-request delay in normal operation. On 429 response, pause workflow execution for 1 second and retry up to 5 times. If still failing after 5 retries, log to error_log and post Slack alert. Exponential backoff: 1s, 2s, 4s, 8s, 16s.
Jamf Pro API
Bearer token expired mid-workflow (token older than 30 minutes)
Orchestration layer refreshes token proactively at 25-minute intervals. If a 401 is returned mid-call, attempt one token refresh via POST /api/v1/auth/token. Retry original call once. If second 401 is returned, halt workflow, log to error_log, and post Slack alert. Do not issue a wipe command without a confirmed valid token.
Jamf Pro API
Device serial number not found in Jamf (enrollment gap on assignment)
Write jamf_enrollment_status='Not Enrolled' to Airtable. Post Slack alert to IT channel flagging the device serial and assigned employee for manual IT review. Do not proceed with assignment confirmation email until enrollment is confirmed. Workflow branch halts awaiting manual resolution.
Jamf Pro API
Remote wipe command accepted but managementStatus does not reach 'Erased' within 60 minutes
Poll every 5 minutes for up to 60 minutes (12 polls). If status does not reach 'Erased', write jamf_enrollment_status='Wipe Timeout' to Airtable. Post Slack alert to IT manager (DM) with device serial and last known status. Do not mark device as 'Available' in Airtable. Manual IT intervention required.
Gmail API
HTTP 403 on message send (insufficient OAuth scope or delegation not granted)
Log full error to error_log. Post Slack alert to IT channel with recipient address and template name. Do not retry: a 403 indicates a configuration error (missing delegation or wrong scope) that must be corrected in the Google Workspace Admin console. Mark Airtable comms_sent as FALSE.
Gmail API
Pub/Sub push for audit reply received but thread ID does not match any open audit record in Airtable
Log the unmatched message ID to error_log. Do not discard. Post Slack alert to IT channel with the sender address and message snippet for manual review. The IT coordinator reviews unmatched replies and updates Airtable manually if the response is valid.
Slack API
HTTP 404: channel or user ID not found (e.g. IT manager has left workspace)
Log error to Airtable error_log with the target channel or user ID. Attempt fallback: post to SLACK_IT_CHANNEL_ID if the original target was a DM. If channel post also fails, send a Gmail notification to GMAIL_SENDER_ADDRESS as a last-resort alert. Update credential store with correct IDs before next run.
Google Sheets fallback poll
Spreadsheet schema mismatch: expected columns missing or in wrong order
Log column mismatch to error_log including the actual vs expected header row. Skip all rows in the affected poll run. Post Slack alert to IT channel. Do not create any Airtable records from a schema-mismatched sheet. Workflow retries on the next scheduled poll after manual schema correction.
All errors logged to the Airtable error_log table must include: timestamp (UTC), workflow name, agent number, integration name, HTTP status code or error type, affected record ID or event key, and the raw error message string. This table is the primary debugging surface for the FullSpec team during the monitored go-live period. Contact support@gofullspec.com if any error pattern persists beyond two consecutive runs.
Integration and API SpecPage 4 of 4