Back to Interview Scheduling

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

Interview Scheduling Automation

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

This document is the authoritative technical reference for every integration point in the Interview Scheduling automation. It covers authentication methods, exact OAuth scopes, webhook configuration, field mappings between agents, credential store layout, and defined failure behaviours. The FullSpec team uses this spec to build, configure, and verify every connection. Nothing in this document should be changed without updating the corresponding test cases in the companion Test and QA Plan.

01Tool inventory

Tool
Role in automation
Auth method
Min plan required
Used by agents
Greenhouse
ATS trigger source and candidate record write-back
API key (Basic Auth over HTTPS)
Growth or above (Harvest API required for write-back)
Agent 1 (trigger), Agent 3
Notion
Interview panel rules and interviewer configuration store
Notion Integration Token (OAuth 2.0 internal integration)
Free tier sufficient; Plus recommended for permissions control
Agent 1
Google Calendar
Interviewer availability query and calendar event creation
OAuth 2.0 (service account per interviewer or delegated domain auth)
Google Workspace Business Starter or personal accounts
Agent 1, Agent 2
Gmail
Candidate outreach, booking link delivery, and day-before reminder
OAuth 2.0 (delegated Gmail send-as scope)
Google Workspace (same account as Calendar auth)
Agent 2
Calendly
Candidate self-booking, slot management, and reschedule handling
OAuth 2.0 (personal access token also supported)
Teams plan required for availability scoping per interviewer
Agent 2
Slack
Interviewer briefing notifications on confirmed booking
OAuth 2.0 (Slack app with Bot Token)
Free tier sufficient for bot messaging
Agent 2
Automation platform (orchestration layer)
Workflow orchestration, credential store, retry logic, and agent sequencing
Internal credential store; per-tool OAuth tokens injected at runtime
Determined at build; must support webhook ingestion and scheduled triggers
All agents
Before you connect anything: validate every integration in a sandbox or test environment before entering production credentials. For Greenhouse, use a test candidate record in a non-live pipeline stage. For Google Calendar and Gmail, use a dedicated service account rather than a personal recruiter account. For Slack, install the app into a private test channel first. Calendly sandbox accounts are available on paid plans under Settings > Integrations. No production credentials should be stored or used until all sandbox tests pass.
Integration and API SpecPage 1 of 5
FS-DOC-05Technical

02Per-tool integration detail

Greenhouse

Greenhouse acts as both the automation trigger source (via inbound webhook) and the write-back destination for confirmed interview data. The Harvest API v2 is required for candidate record updates. The Job Boards API is not used in this automation.

Auth method
API key transmitted as Basic Auth: base64-encode 'API_KEY:' (note the trailing colon, no password). Pass in Authorization header on every request.
Required scopes
Harvest API key permissions: candidates:read, candidates:write, applications:read, jobs:read, interviews:read, interviews:write. These are set in Greenhouse > Dev Center > API Credential Management.
Webhook setup
Configure a webhook in Greenhouse > Configure > Dev Center > Web Hooks. Event: 'Candidate Stage Change'. Set the endpoint to the orchestration layer's inbound webhook URL. Enable HMAC-SHA256 signature validation using the secret key generated in Greenhouse. The signature is passed in the Signature header as 'sha256=<hex_digest>'. Validate on every inbound request before processing.
Required configuration
Store the target interview stage name(s) exactly as they appear in Greenhouse (e.g. 'Phone Screen', 'Technical Interview') in the credential store as GREENHOUSE_INTERVIEW_STAGES (comma-separated string). Store the Harvest API key as GREENHOUSE_API_KEY and the webhook secret as GREENHOUSE_WEBHOOK_SECRET. Do not hardcode any of these values.
Rate limits
Harvest API: 50 requests per 10 seconds per API key. At ~15 rounds/month the automation makes at most 3-4 API calls per trigger event. No throttling needed at current volume. Monitor if volume exceeds 200 rounds/month.
Constraints
Write-back to candidate records requires the 'interviews:write' permission, which is only available on Growth plan and above. Confirm plan tier before build. Greenhouse does not expose free-busy data; calendar availability must be queried via Google Calendar API directly.
// Inbound webhook payload (trigger)
POST /webhooks/greenhouse
Headers: Signature: sha256=<hex>, Content-Type: application/json
{
  "action": "candidate_stage_change",
  "payload": {
    "application": {
      "id": 123456789,
      "candidate_id": 987654321,
      "current_stage": { "id": 111, "name": "Technical Interview" },
      "job_id": 222333
    }
  }
}

// Write-back call (Agent 3 output)
PATCH /v1/applications/{application_id}/interviews/{interview_id}
{
  "scheduled_at": "2025-05-14T10:00:00Z",
  "interviewers": [ { "user_id": 441 }, { "user_id": 442 } ],
  "status": "scheduled"
}
Notion

Notion stores the interview panel rules database: which interviewers are assigned per job stage, interview duration, format (video/in-person), and any panel constraints. The automation reads this database on every trigger; it does not write to Notion.

Auth method
Notion Internal Integration Token (Bearer token). Created at notion.so/my-integrations. The integration must be explicitly shared with the panel rules database page in Notion by a workspace admin.
Required scopes
Notion integration capabilities: Read content. No insert/update content capability required. No comment capabilities required. Token stored as NOTION_API_TOKEN.
Webhook setup
Not applicable. Notion is polled on-demand each time a Greenhouse webhook fires. No Notion webhook is configured.
Required configuration
The panel rules Notion database must contain these exact property names: Job Stage (title), Interviewers (relation to People db or multi-select of interviewer email strings), Duration Minutes (number), Interview Format (select: Video/In-Person), Panel Constraints (rich text). Store the database ID as NOTION_PANEL_DB_ID in the credential store. The database ID appears in the Notion page URL between the workspace slug and the query string.
Rate limits
Notion API: 3 requests per second average, burst to 10. At ~15 triggers/month the polling load is negligible. No throttling logic required at current volume.
Constraints
Notion API returns rich text as an array of text objects; the orchestration layer must flatten these to plain strings before passing to downstream agents. Relation properties return page IDs, not email addresses; if using relations, a secondary lookup call to the People database is required to resolve interviewer email addresses. Consider using a multi-select property of email strings to avoid the extra call.
// Query panel rules for a given job stage
POST https://api.notion.com/v1/databases/{NOTION_PANEL_DB_ID}/query
Headers: Authorization: Bearer {NOTION_API_TOKEN}, Notion-Version: 2022-06-28
{
  "filter": {
    "property": "Job Stage",
    "title": { "equals": "Technical Interview" }
  }
}

// Expected response fields extracted
results[0].properties.Interviewers       -> array of interviewer emails
results[0].properties["Duration Minutes"] -> integer (e.g. 60)
results[0].properties["Interview Format"] -> string (e.g. "Video")
results[0].properties["Panel Constraints"]-> string (e.g. "No back-to-back slots")
Google Calendar

Google Calendar is queried to retrieve interviewer free/busy windows, and is then written to in order to create the confirmed interview event with all participants attached. Both read and write operations occur within the same OAuth token scope.

Auth method
OAuth 2.0. Recommended approach: Google Workspace service account with domain-wide delegation enabled, so the automation can query and write calendars on behalf of any interviewer in the organisation without individual consent flows. Store the service account JSON key as GOOGLE_SERVICE_ACCOUNT_JSON. Impersonate each interviewer using the sub parameter in the JWT claim.
Required scopes
https://www.googleapis.com/auth/calendar.readonly (availability query), https://www.googleapis.com/auth/calendar.events (event creation and update). Both scopes must be granted in the Google Workspace admin console under Security > API Controls > Domain-wide Delegation.
Webhook setup
Not used for triggering. Google Calendar push notifications (watch channels) are not required in this build. The automation queries free/busy on-demand when the Greenhouse webhook fires.
Required configuration
Store the service account JSON key path or content as GOOGLE_SERVICE_ACCOUNT_JSON. Store the Google Calendar ID for the recruiter's scheduling calendar (used as the organiser) as GOOGLE_RECRUITER_CALENDAR_ID. Store the video conferencing provider preference as GOOGLE_CONFERENCE_SOLUTION (e.g. 'hangoutsMeet'). Do not hardcode interviewer email addresses; resolve them from Notion at runtime.
Rate limits
Calendar API: 1,000,000 queries/day per project; 10,000 requests/100 seconds per user. At 15 rounds/month with 2-3 interviewers each, peak usage is approximately 90 API calls/month. No throttling required. A 500ms delay between per-user free/busy calls is sufficient to avoid per-user rate collisions.
Constraints
Free/busy API returns blocked time only, not event details, which is correct for privacy. The automation must compute overlap windows client-side from the free/busy response. Event creation must include the 'sendUpdates: all' parameter so Google sends native calendar invitations to all attendees. Conference link generation requires the conferenceDataVersion=1 parameter on event insert.
// Free/busy query for slot computation
POST https://www.googleapis.com/calendar/v3/freeBusy
{
  "timeMin": "2025-05-14T08:00:00Z",
  "timeMax": "2025-05-16T18:00:00Z",
  "timeZone": "America/New_York",
  "items": [
    { "id": "interviewer1@yourcompany.com" },
    { "id": "interviewer2@yourcompany.com" }
  ]
}

// Calendar event creation (on Calendly booking confirmed)
POST https://www.googleapis.com/calendar/v3/calendars/{GOOGLE_RECRUITER_CALENDAR_ID}/events?conferenceDataVersion=1&sendUpdates=all
{
  "summary": "Technical Interview: [Candidate Name]",
  "start": { "dateTime": "2025-05-14T10:00:00Z" },
  "end":   { "dateTime": "2025-05-14T11:00:00Z" },
  "attendees": [ { "email": "candidate@email.com" }, { "email": "interviewer1@yourcompany.com" } ],
  "conferenceData": { "createRequest": { "requestId": "unique-uuid", "conferenceSolutionKey": { "type": "hangoutsMeet" } } }
}
Integration and API SpecPage 2 of 5
FS-DOC-05Technical
Gmail

Gmail is used for two distinct outbound sends: the initial booking link email to the candidate, and the timed day-before reminder. Both are sent from the recruiter's address using delegated OAuth access. No inbound Gmail parsing is required in this build.

Auth method
OAuth 2.0 with delegated authority via the same Google service account used for Calendar, or a separate OAuth 2.0 client credential for Gmail. Impersonate the recruiter's Google account using the sub claim. Store the sending address as GMAIL_SENDER_ADDRESS.
Required scopes
https://www.googleapis.com/auth/gmail.send (send-only; do not request broader mail scopes). This scope must be included in the domain-wide delegation grant alongside the Calendar scopes.
Webhook setup
Not applicable. Gmail is outbound-only in this automation. No Gmail push notification or polling is configured. The Calendly webhook handles the booking confirmation signal.
Required configuration
Store email template IDs or template strings in the credential store as GMAIL_TEMPLATE_BOOKING_LINK and GMAIL_TEMPLATE_REMINDER. Templates must use these exact placeholder tokens: {{candidate_first_name}}, {{calendly_link}}, {{interview_date}}, {{interview_time}}, {{meet_link}}, {{job_title}}. Do not hardcode candidate names or links in template bodies. Store the reply-to address as GMAIL_REPLY_TO_ADDRESS.
Rate limits
Gmail API send limit: 100 messages/second per user (practically: 2,000 messages/day via API). At 15 rounds/month with 2 sends per round, peak volume is 30 messages/month. No throttling required.
Constraints
The reminder send must be scheduled as a delayed action 20 hours before the interview start time, not sent immediately on booking. The orchestration layer must store the reminder job with the interview datetime and execute it at the correct offset. If the interview is rescheduled, the pending reminder job must be cancelled and a new one queued for the new datetime.
// Send booking link email
POST https://gmail.googleapis.com/gmail/v1/users/{GMAIL_SENDER_ADDRESS}/messages/send
Body: RFC 2822 message, base64url-encoded
To: candidate@email.com
Subject: Your interview for {{job_title}} - please book your slot
Body: Hi {{candidate_first_name}}, please use this link to book: {{calendly_link}}

// Scheduled reminder (queued at booking time, fires -20h before start)
To: candidate@email.com
Subject: Reminder: Your interview is tomorrow
Body: Hi {{candidate_first_name}}, your interview is on {{interview_date}} at {{interview_time}}.
      Join here: {{meet_link}}
Calendly

Calendly manages the candidate-facing booking experience. The automation generates a one-time scheduling link scoped to pre-computed available windows and monitors for the booking confirmation webhook. Rescheduling through Calendly re-fires the confirmation webhook with updated event data.

Auth method
OAuth 2.0 (preferred for multi-user organisation access) or a Personal Access Token for single-org builds. Store as CALENDLY_ACCESS_TOKEN. Organisation URI stored as CALENDLY_ORG_URI (format: https://api.calendly.com/organizations/{uuid}).
Required scopes
Calendly OAuth scopes: default (covers event_types:read, scheduled_events:read, scheduling_links:write, webhook_subscriptions:write). No additional scope strings are declared separately by Calendly; the default scope bundle covers all required operations. Teams plan required to enable per-host availability scoping.
Webhook setup
Register a webhook subscription via POST https://api.calendly.com/webhook_subscriptions. Events to subscribe: invitee.created (booking confirmed), invitee.canceled (booking cancelled or rescheduled). Set the listener URL to the orchestration layer's Calendly webhook endpoint. Calendly signs payloads with HMAC-SHA256 using the webhook signing key available in the webhook subscription response; validate the Calendly-Webhook-Signature header on every inbound call. Store the signing key as CALENDLY_WEBHOOK_SIGNING_KEY.
Required configuration
Store the base event type UUID (the interview scheduling event type) as CALENDLY_EVENT_TYPE_UUID. When generating a single-use scheduling link, pass max_event_count: 1 and set the available_times parameter to the slot windows computed by the Availability Matching Agent. Store the scheduling page slug base as CALENDLY_SLUG_BASE. Do not expose the raw event type UUID in candidate-facing URLs; use the generated single-use link only.
Rate limits
Calendly API: 100 requests/minute. At 15 rounds/month, peak is well under 1 request/minute on average. No throttling required. Link generation is a single API call per trigger event.
Constraints
Single-use scheduling links expire after the max_event_count is reached or after a configurable TTL (set to 7 days; store as CALENDLY_LINK_TTL_DAYS). If the candidate does not book within the TTL, the orchestration layer must detect link expiry and flag the exception to the recruiter. Calendly does not natively restrict available slots to the windows computed externally; the automation must configure the event type's availability override or use a pre-configured restricted event type that matches the computed windows.
// Generate single-use scheduling link
POST https://api.calendly.com/scheduling_links
{
  "max_event_count": 1,
  "owner": "https://api.calendly.com/event_types/{CALENDLY_EVENT_TYPE_UUID}",
  "owner_type": "EventType"
}
Response -> booking_url passed to Gmail send step

// Inbound webhook on booking confirmed
POST /webhooks/calendly
Headers: Calendly-Webhook-Signature: <sha256_sig>
{
  "event": "invitee.created",
  "payload": {
    "event": "https://api.calendly.com/scheduled_events/{event_uuid}",
    "invitee": { "email": "candidate@email.com", "name": "Alex Smith" },
    "start_time": "2025-05-14T10:00:00Z",
    "end_time":   "2025-05-14T11:00:00Z"
  }
}
Slack

Slack is used exclusively for outbound interviewer briefing messages sent immediately after a Calendly booking is confirmed. One message is sent per interviewer. No Slack commands or inbound message handling are required in this build.

Auth method
OAuth 2.0 Slack app with Bot Token (xoxb-). Install the Slack app to the workspace via the App Directory or a direct install URL. Store the bot token as SLACK_BOT_TOKEN and the workspace ID as SLACK_WORKSPACE_ID.
Required scopes
Bot token scopes: chat:write (post messages to channels or DMs), users:read.email (look up a Slack user by email address to resolve the interviewer's Slack ID for DM delivery). Both scopes must be declared in the Slack app manifest before installation.
Webhook setup
Not applicable for this build. Slack is outbound-only. Incoming Webhooks are not used; the bot sends messages via the chat.postMessage API method to enable per-user DM delivery using looked-up Slack user IDs.
Required configuration
Store the fallback notification channel (used when a Slack user ID cannot be resolved for an interviewer) as SLACK_FALLBACK_CHANNEL_ID. Store the message template string as SLACK_BRIEFING_TEMPLATE with placeholders: {{interviewer_first_name}}, {{candidate_full_name}}, {{interview_date}}, {{interview_time}}, {{job_title}}, {{cv_link}}, {{focus_area}}. The CV link is sourced from Greenhouse (candidate profile URL). The focus area is sourced from the Notion panel rules record.
Rate limits
Slack Web API: tier 3 methods including chat.postMessage allow 50 requests/minute. At 15 rounds/month with 2 interviewers per round, peak is 30 messages/month. No throttling required. Add a 200ms delay between messages if sending to more than 10 interviewers in a single event.
Constraints
The users:read.email scope requires the Slack app to be approved by a workspace admin. If an interviewer's email does not match a Slack workspace member, the message must fall back to the SLACK_FALLBACK_CHANNEL_ID with the interviewer name included in the message body. This failure path must be handled explicitly and must not cause the overall workflow to halt.
// Resolve interviewer Slack user ID from email
GET https://slack.com/api/users.lookupByEmail?email=interviewer1@yourcompany.com
Headers: Authorization: Bearer {SLACK_BOT_TOKEN}
Response -> user.id used as channel in postMessage

// Send briefing DM
POST https://slack.com/api/chat.postMessage
{
  "channel": "U01ABCDEF",
  "text": "Hi {{interviewer_first_name}}, you have an interview with {{candidate_full_name}} on {{interview_date}} at {{interview_time}}. CV: {{cv_link}}. Focus area: {{focus_area}}"
}
Integration and API SpecPage 3 of 5
FS-DOC-05Technical

03Field mappings between tools

The following tables document every field handoff between agents and tools. Source and destination field names are written exactly as they appear in each tool's API response or request body. All intermediate values are stored in the workflow execution context object between steps.

Handoff 1: Greenhouse webhook to Availability Matching Agent (Agent 1)

Source tool
Source field
Destination tool
Destination field
Greenhouse
`payload.application.id`
Orchestration context
`ctx.application_id`
Greenhouse
`payload.application.candidate_id`
Orchestration context
`ctx.candidate_id`
Greenhouse
`payload.application.current_stage.name`
Notion query filter
`filter.title.equals`
Greenhouse
`payload.application.job_id`
Orchestration context
`ctx.job_id`

Handoff 2: Notion panel rules to Google Calendar free/busy query (Agent 1 internal)

Source tool
Source field
Destination tool
Destination field
Notion
`results[].properties.Interviewers`
Google Calendar free/busy
`items[].id` (email per interviewer)
Notion
`results[].properties["Duration Minutes"]`
Orchestration context
`ctx.interview_duration_minutes`
Notion
`results[].properties["Interview Format"]`
Orchestration context
`ctx.interview_format`
Notion
`results[].properties["Panel Constraints"]`
Orchestration context
`ctx.panel_constraints`

Handoff 3: Availability Matching Agent output to Scheduling and Comms Agent (Agent 1 to Agent 2)

Source tool
Source field
Destination tool
Destination field
Orchestration context
`ctx.computed_slots[]`
Calendly link generation
Scoped availability window config
Orchestration context
`ctx.candidate_id`
Greenhouse candidate lookup
`candidate_id` path param
Orchestration context
`ctx.interview_duration_minutes`
Calendly event type config
`duration` (minutes)
Orchestration context
`ctx.interviewer_emails[]`
Google Calendar event creation
`attendees[].email`

Handoff 4: Calendly booking webhook to Google Calendar event creation and Gmail send (Agent 2 internal)

Source tool
Source field
Destination tool
Destination field
Calendly
`payload.start_time`
Google Calendar event
`start.dateTime`
Calendly
`payload.end_time`
Google Calendar event
`end.dateTime`
Calendly
`payload.invitee.email`
Google Calendar event
`attendees[0].email`
Calendly
`payload.invitee.name`
Gmail template
`{{candidate_full_name}}`
Calendly
`payload.invitee.email`
Gmail send
`To` header
Calendly
`payload.start_time`
Gmail template
`{{interview_date}}` and `{{interview_time}}`
Calendly
`payload.start_time`
Orchestration scheduler
Reminder job trigger offset (-20 hours)

Handoff 5: Google Calendar event creation response to Slack and Gmail reminder (Agent 2 internal)

Source tool
Source field
Destination tool
Destination field
Google Calendar
`hangoutLink`
Gmail template
`{{meet_link}}`
Google Calendar
`hangoutLink`
Slack template
`{{meet_link}}`
Google Calendar
`id` (event ID)
Orchestration context
`ctx.calendar_event_id`
Orchestration context
`ctx.cv_link`
Slack template
`{{cv_link}}`
Notion
`results[].properties["Panel Constraints"]`
Slack template
`{{focus_area}}`

Handoff 6: Confirmed booking data to Greenhouse write-back (Agent 2 to Agent 3)

Source tool
Source field
Destination tool
Destination field
Calendly
`payload.start_time`
Greenhouse interview PATCH
`scheduled_at`
Orchestration context
`ctx.interviewer_user_ids[]`
Greenhouse interview PATCH
`interviewers[].user_id`
Orchestration context
`ctx.application_id`
Greenhouse interview PATCH
URL path param `application_id`
Orchestration context
`ctx.greenhouse_interview_id`
Greenhouse interview PATCH
URL path param `interview_id`

04Build stack and orchestration

Orchestration layout
Three discrete workflows, one per agent: Workflow 1 (Availability Matching Agent), Workflow 2 (Scheduling and Comms Agent), Workflow 3 (ATS Update Agent). Workflows 2 and 3 are triggered by webhook events (Calendly booking confirmed), not by chaining from Workflow 1. Workflow 1 outputs the Calendly booking link and terminates; Workflows 2 and 3 re-activate independently on the Calendly webhook.
Shared credential store
All API keys, OAuth tokens, webhook secrets, template strings, and configuration constants are stored in the orchestration platform's centralised encrypted credential store. No value is hardcoded in any workflow step. Credentials are injected at runtime by reference name only.
Agent 1 trigger (Availability Matching Agent)
Inbound webhook from Greenhouse. The orchestration layer exposes a public HTTPS endpoint. Greenhouse posts a signed payload on each candidate stage change. The platform validates the HMAC-SHA256 signature using GREENHOUSE_WEBHOOK_SECRET before any processing begins. Invalid signatures are rejected with HTTP 401 and logged.
Agent 2 trigger (Scheduling and Comms Agent)
Inbound webhook from Calendly. Fires on invitee.created (booking confirmed) and invitee.canceled (rescheduled or cancelled). The orchestration layer validates the Calendly-Webhook-Signature header using CALENDLY_WEBHOOK_SIGNING_KEY before processing. On invitee.canceled, the workflow checks whether a new booking has been created (reschedule) or whether the event is a clean cancellation, and branches accordingly.
Agent 3 trigger (ATS Update Agent)
Triggered as the final step of Workflow 2 (internal call, not an external webhook). Receives the confirmed booking context object from the execution context of Workflow 2. Alternatively may be implemented as a sub-workflow called synchronously at the end of Workflow 2.
Reminder scheduling
The day-before Gmail reminder is implemented as a delayed/scheduled task enqueued within the orchestration platform at the time of booking confirmation. The job fires at a calculated time: interview_start_time minus 20 hours. If the interview is rescheduled, the original scheduled job must be cancelled by job ID and a new job enqueued for the new time.
Retry policy (global default)
All HTTP calls to external APIs: 3 retry attempts with exponential backoff starting at 2 seconds (2s, 4s, 8s). On third failure, the step is marked as failed and an alert is sent to SLACK_FALLBACK_CHANNEL_ID. Unhandled exceptions must never fail silently.
Logging
Each workflow execution logs: trigger timestamp, candidate_id, application_id, each step outcome (success/failure/skipped), and any API error codes received. Logs are retained for 90 days within the orchestration platform.
All credential references are injected at runtime. No plaintext credential values appear in workflow step definitions.
// Credential store contents (reference names only; values stored encrypted)

GREENHOUSE_API_KEY            // Harvest API key, Basic Auth
GREENHOUSE_WEBHOOK_SECRET     // HMAC-SHA256 secret for inbound webhook validation
GREENHOUSE_INTERVIEW_STAGES   // Comma-separated stage names to act on (e.g. 'Technical Interview,Phone Screen')

NOTION_API_TOKEN              // Internal integration bearer token
NOTION_PANEL_DB_ID            // Database ID of the interview panel rules Notion database

GOOGLE_SERVICE_ACCOUNT_JSON   // Full service account key JSON (base64-encoded string)
GOOGLE_RECRUITER_CALENDAR_ID  // Calendar ID used as event organiser (recruiter's calendar)
GOOGLE_CONFERENCE_SOLUTION    // Conference type key e.g. 'hangoutsMeet'

GMAIL_SENDER_ADDRESS          // Recruiter email address used as sending identity
GMAIL_REPLY_TO_ADDRESS        // Reply-to address for candidate replies
GMAIL_TEMPLATE_BOOKING_LINK   // Full email body template string with {{placeholders}}
GMAIL_TEMPLATE_REMINDER       // Day-before reminder email body template string

CALENDLY_ACCESS_TOKEN         // OAuth 2.0 bearer token or personal access token
CALENDLY_ORG_URI              // Organisation URI: https://api.calendly.com/organizations/{uuid}
CALENDLY_EVENT_TYPE_UUID      // UUID of the interview scheduling event type
CALENDLY_WEBHOOK_SIGNING_KEY  // Signing key for Calendly-Webhook-Signature validation
CALENDLY_LINK_TTL_DAYS        // Scheduling link expiry window in days (default: 7)
CALENDLY_SLUG_BASE            // Base URL slug for Calendly scheduling pages

SLACK_BOT_TOKEN               // xoxb- prefixed bot token
SLACK_WORKSPACE_ID            // Slack workspace identifier
SLACK_FALLBACK_CHANNEL_ID     // Channel ID for unresolvable DM targets and error alerts
SLACK_BRIEFING_TEMPLATE       // Interviewer briefing message template string with {{placeholders}}
Integration and API SpecPage 4 of 5
FS-DOC-05Technical

05Error handling and retry logic

Unhandled exceptions must never fail silently. Every integration point in this automation must have a defined failure behaviour. If a step exhausts its retry attempts, the orchestration layer must log the failure, send an alert to SLACK_FALLBACK_CHANNEL_ID with the candidate_id and step name, and halt further processing for that workflow run without affecting other concurrent runs.
Integration
Scenario
Required behaviour
Greenhouse (inbound webhook)
Webhook signature validation fails (tampered or replayed payload)
Reject immediately with HTTP 401. Log the raw headers and payload. Do not process. Send alert to SLACK_FALLBACK_CHANNEL_ID. No retry.
Greenhouse (inbound webhook)
Stage change fires for a stage not in GREENHOUSE_INTERVIEW_STAGES
Silently discard the event with an HTTP 200 response. Log the stage name and candidate_id at debug level. No alert required.
Notion (panel rules query)
No matching record found for the triggering job stage
Halt Workflow 1. Log the unmatched stage name. Send Slack alert to SLACK_FALLBACK_CHANNEL_ID: 'No panel rules found for stage [name], candidate [id]. Manual scheduling required.' Do not send a booking link to the candidate.
Notion (panel rules query)
Notion API returns 429 (rate limit) or 503 (service unavailable)
Retry up to 3 times with exponential backoff (2s, 4s, 8s). On third failure, halt Workflow 1 and send Slack alert. Log full API error response.
Google Calendar (free/busy query)
One or more interviewer calendars return no available slots in the search window
Do not send a booking link. Send Slack alert to SLACK_FALLBACK_CHANNEL_ID: 'No overlapping slots found for candidate [id], stage [name]. Recruiter action required.' Log the queried time range and all busy blocks returned.
Google Calendar (free/busy query)
Service account authentication fails (expired token or delegation revoked)
Halt Workflow 1 immediately. Send critical Slack alert: 'Google Calendar auth failure. Workflows halted.' Log the OAuth error code. No retry until credentials are refreshed manually. All subsequent triggers will also fail until resolved.
Google Calendar (event creation)
Event creation returns 409 conflict (slot no longer available)
Retry once after 5 seconds. If conflict persists, cancel the calendar event attempt, send Slack alert to recruiter: 'Slot conflict on booking for candidate [id]. Manual calendar event creation required.' Log the conflicting event details.
Gmail (booking link send)
Gmail API returns 403 (insufficient permissions or send quota exceeded)
Retry once after 10 seconds. On second failure, log the error and send Slack alert to SLACK_FALLBACK_CHANNEL_ID: 'Booking link email failed for candidate [id]. Manual send required.' Include the candidate email address and generated Calendly link in the alert so the recruiter can send manually.
Calendly (link generation)
API returns 422 (invalid event type UUID or availability config error)
Do not retry. Log the full API error response. Send Slack alert with the error detail. Halt Workflow 1 for this run. Escalate to FullSpec support at support@gofullspec.com if the error recurs.
Calendly (inbound booking webhook)
Candidate does not book within CALENDLY_LINK_TTL_DAYS window (link expires)
The orchestration layer must detect link expiry via a scheduled check job created at link generation time. On expiry, send Slack alert to SLACK_FALLBACK_CHANNEL_ID: 'Scheduling link expired for candidate [id]. Follow-up required.' Log the expiry timestamp and original link generation time.
Slack (interviewer briefing DM)
users.lookupByEmail returns no match for an interviewer email
Fall back to posting the briefing message to SLACK_FALLBACK_CHANNEL_ID, explicitly naming the intended interviewer in the message body. Log the unresolved email address. Do not halt the workflow; continue to remaining interviewers and downstream steps.
Greenhouse (ATS write-back)
PATCH interview returns 404 (interview ID not found) or 403 (insufficient API permissions)
Retry once after 5 seconds. On second failure, log the full error and send Slack alert: 'Greenhouse write-back failed for candidate [id]. Manual ATS update required.' Include the confirmed interview datetime and interviewer names in the alert to enable manual entry. Do not halt the candidate-facing flow; the booking is still confirmed.
For any integration failure that cannot be resolved automatically, the recruiter receives a Slack alert with enough context to take manual action immediately. The candidate-facing booking experience is isolated from internal write-back failures: a Greenhouse write-back failure must not cause the booking confirmation email or calendar invite to be withheld. Contact support@gofullspec.com for any persistent integration errors that cannot be resolved with the guidance in this document.
Integration and API SpecPage 5 of 5

More documents for this process

Every document generated for Interview Scheduling.

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