Back to Job / Project 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

Job / Project Scheduling Automation

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

This document is the definitive technical reference for every integration point in the Job / Project Scheduling automation. It covers tool authentication, exact OAuth scopes, webhook configuration, field mappings between agents, credential storage conventions, and defined failure behaviours. The FullSpec team uses this specification during build and QA. The process owner's team does not need to act on this document directly, but should retain it as the authoritative record of how all tools are connected and what each connection requires.

01Tool inventory

Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
Jobber
Job intake trigger and record update (source of truth)
OAuth 2.0
Core ($69/mo)
Agent 1
Google Calendar
Crew availability query and calendar event creation
OAuth 2.0 (Google)
Free (Google Workspace or personal)
Agent 1
HubSpot
CRM deal and contact record update with scheduled date
OAuth 2.0 (private app token)
Starter ($50/mo)
Agent 2
Slack
Crew job assignment notification
OAuth 2.0 (Bot token)
Pro ($8/mo)
Agent 2
Gmail
Customer confirmation email dispatch
OAuth 2.0 (Google)
Free (Google Workspace or personal)
Agent 2
Workflow Automation Platform
Orchestration layer: hosts all agent workflows, credential store, retry logic, and inter-agent data passing
Platform-native (API key per connected service)
Paid tier sufficient for webhook listeners and multi-step workflows
Agents 1 and 2
Before you connect anything: all integrations must be authenticated and smoke-tested against each tool's sandbox or developer environment before any production credentials are entered into the credential store. Confirm that each OAuth token returns the expected scopes and that test API calls resolve successfully before proceeding to build.

02Per-tool integration detail

Jobber

Jobber is the automation entry point. A webhook fires when a job status transitions to confirmed, delivering the full job payload to the orchestration layer. The platform also writes back to Jobber via the REST API to update the assigned crew and confirmed schedule time on the job record.

Auth method
OAuth 2.0. Register an application in the Jobber Developer Portal. Exchange the authorization code for an access token and refresh token. Store both in the credential store (see Section 04). Access tokens expire after 60 minutes; the orchestration layer must use the refresh token to obtain a new one automatically.
Required scopes
read:jobs, write:jobs, read:clients, read:work_orders
Webhook / trigger setup
Register a webhook subscription via POST /webhooks in the Jobber REST API. Topic: job.updated. Filter on status = 'confirmed'. The endpoint URL is the orchestration layer's inbound webhook URL (unique per workflow). Jobber signs each webhook payload with HMAC-SHA256 using the shared webhook secret stored as JOBBER_WEBHOOK_SECRET in the credential store. The orchestration layer must verify this signature before processing any payload. Reject and log any request where the signature does not match.
Required configuration
The Jobber job record must include: job_id, client.name, client.email, client.phone, site_address (full), scheduled_start (ISO 8601), scheduled_end (ISO 8601), job_title, and custom_fields.special_instructions. The webhook payload must be validated for presence of all these fields before downstream steps execute. If any required field is absent, the workflow halts and raises an exception alert.
Write-back endpoint
PATCH /jobs/{job_id}. Payload fields updated by Agent 1: assigned_employee_ids (array), scheduled_start, scheduled_end, status. Payload field updated by the orchestration layer after Agent 2 completes: internal_note (appended with 'Notifications dispatched at {timestamp}').
Rate limits
Jobber enforces 200 requests per minute per OAuth application. Current volume is approximately 40 to 80 confirmed jobs per month, equating to well under 5 API calls per minute at peak. No throttling logic is required at current volume. If volume grows past 500 jobs per month, implement a request queue with 300ms minimum spacing between calls.
Constraints
Jobber webhooks do not support payload filtering at the API level beyond topic selection. The orchestration layer must filter on status = 'confirmed' in its own conditional logic. Webhook delivery is not guaranteed; implement an idempotency key (job_id + updated_at timestamp) to prevent duplicate processing if a payload is delivered more than once.
Google Calendar

Google Calendar serves two functions: querying crew member calendars for availability within the requested job window, and creating the confirmed calendar event once a crew member is assigned. Each crew member must have their individual Google Calendar shared with the service account used by the integration.

Auth method
OAuth 2.0 using a Google Cloud service account with domain-wide delegation, or individual OAuth consent per crew calendar. Recommended approach for multi-crew access: service account with domain-wide delegation scoped to each crew member's calendar. Store the service account JSON key as GCAL_SERVICE_ACCOUNT_KEY in the credential store.
Required scopes
https://www.googleapis.com/auth/calendar.readonly (availability queries), https://www.googleapis.com/auth/calendar.events (event creation and updates)
Webhook / trigger setup
No inbound webhook required from Google Calendar. The orchestration layer polls Google Calendar on demand using the freebusy query endpoint when a Jobber webhook is received. Push notifications from Google Calendar (via the watch endpoint) are not used in this integration.
Availability query endpoint
POST https://www.googleapis.com/calendar/v3/freeBusy. Body: { timeMin: scheduled_start, timeMax: scheduled_end, items: [ { id: crew_calendar_id }, ... ] }. The calendar IDs for all eligible crew members are stored as a JSON array under GCAL_CREW_CALENDAR_IDS in the credential store. The response is parsed to identify which calendar IDs have no busy blocks in the requested window.
Event creation endpoint
POST https://www.googleapis.com/calendar/v3/calendars/{calendarId}/events. Required fields: summary (job_title + client.name), location (site_address), start.dateTime, end.dateTime, description (job_id, special_instructions, client.phone), attendees (crew member email). Event ID returned is stored for potential update or deletion on reschedule.
Required configuration
GCAL_CREW_CALENDAR_IDS must be populated before go-live and updated whenever a crew member joins or leaves. Each entry maps a crew member's name to their calendar ID. Assignment preference order (e.g. proximity zone, skill match) is defined in a separate rules object stored as GCAL_ASSIGNMENT_RULES in the credential store, not hardcoded in the workflow.
Rate limits
Google Calendar API enforces 1,000,000 queries per day per project and 100 requests per 100 seconds per user. At 40 to 80 jobs per month, each triggering approximately 3 to 8 freebusy queries and 1 event creation, peak load is negligible. No throttling required at current volume.
Constraints
Crew calendars must be kept accurate for availability checks to be reliable. If a crew member has not blocked personal or leave time in Google Calendar, the system will treat them as available. Calendar hygiene onboarding is a prerequisite before go-live. The integration does not read sub-calendar layers; only the primary calendar for each crew member is queried.
HubSpot

HubSpot receives a deal or contact record update from Agent 2 after the crew assignment is confirmed. The scheduled job date and assigned crew name are written to the deal properties so the sales and account team can view current job status without contacting the scheduler.

Auth method
HubSpot private app token (recommended over OAuth for server-to-server integrations). Generate the token in HubSpot Settings > Integrations > Private Apps. Store as HUBSPOT_PRIVATE_APP_TOKEN in the credential store. Rotate this token at least every 180 days.
Required scopes
crm.objects.deals.read, crm.objects.deals.write, crm.objects.contacts.read, crm.objects.contacts.write
Webhook / trigger setup
No inbound webhook from HubSpot is used in this automation. HubSpot is a write destination only. The deal record is looked up by the client email address or HubSpot contact ID (passed from the Jobber payload) and then patched.
Deal lookup endpoint
POST https://api.hubapi.com/crm/v3/objects/deals/search. Filter by association to the contact whose email matches client.email from the Jobber payload. If multiple deals are found, select the one with dealstage matching the pipeline stage IDs stored as HUBSPOT_ACTIVE_PIPELINE_STAGE_IDS in the credential store.
Deal update endpoint
PATCH https://api.hubapi.com/crm/v3/objects/deals/{dealId}. Properties written: scheduled_job_date (custom date property), assigned_crew_member (custom single-line text property), job_id_reference (custom text property). These custom property names must be created in HubSpot before go-live and their internal names stored as HUBSPOT_PROPERTY_NAMES in the credential store.
Required configuration
HUBSPOT_ACTIVE_PIPELINE_STAGE_IDS must list the stage IDs considered active (e.g. appointment_scheduled, in_progress). HUBSPOT_PROPERTY_NAMES must map logical property labels to HubSpot internal names. Do not hardcode stage IDs or property names in workflow logic.
Rate limits
HubSpot Starter enforces 100 API requests per 10 seconds. At current volume (40 to 80 jobs per month), this automation makes approximately 2 API calls per job trigger. No throttling required.
Constraints
If no matching HubSpot deal is found for a given client email, the workflow must log the miss to the exception log and continue without failing the overall workflow. The Slack and Gmail steps must not be blocked by a HubSpot lookup failure.
Integration and API SpecPage 1 of 3
FS-DOC-05Technical
Slack

Slack dispatches job assignment notifications to the assigned crew member or a designated crew channel immediately after the Google Calendar event is created. The message includes the job address, start time, customer name, and any special instructions.

Auth method
Slack OAuth 2.0 Bot Token (xoxb-). Install a Slack app in the workspace via the Slack API portal. Grant the bot token scopes below. Store the bot token as SLACK_BOT_TOKEN in the credential store. The bot must be invited to any channels it posts to.
Required scopes
chat:write, chat:write.public, users:read, users:read.email
Webhook / trigger setup
No inbound webhook from Slack is used. Slack is a write destination. The crew member's Slack user ID is looked up by their email address using GET https://slack.com/api/users.lookupByEmail?email={crew_email} before sending the direct message. The email-to-Slack-ID mapping is resolved at runtime; do not hardcode Slack user IDs.
Message send endpoint
POST https://slack.com/api/chat.postMessage. Key fields: channel (Slack user ID for DM, or channel ID for crew channel), text (fallback), blocks (Block Kit JSON containing job details). The Block Kit template is stored as SLACK_JOB_NOTIFICATION_TEMPLATE in the credential store with named placeholders: {{job_title}}, {{client_name}}, {{site_address}}, {{start_time}}, {{crew_lead}}, {{special_instructions}}.
Required configuration
SLACK_CREW_CHANNEL_ID stores the fallback channel ID used when a direct message lookup fails. SLACK_JOB_NOTIFICATION_TEMPLATE stores the Block Kit JSON template. The bot must be a member of SLACK_CREW_CHANNEL_ID before go-live.
Rate limits
Slack enforces Tier 3 rate limits on chat.postMessage: approximately 50 calls per minute. At 40 to 80 jobs per month, this is far below threshold. No throttling required.
Constraints
If the crew member's email does not match a Slack workspace user, the workflow falls back to posting to SLACK_CREW_CHANNEL_ID and appends the crew member's name in the message body. This fallback must be logged. Slack message delivery does not confirm that the crew member has read the message; delivery confirmation means the API returned ok: true.
Gmail

Gmail sends the customer-facing booking confirmation email after the crew assignment and Slack notification steps complete. The email is sent from the business's Gmail account using a pre-approved template with named placeholders for job-specific details.

Auth method
OAuth 2.0 via a Google Cloud project with the Gmail API enabled. The sending account authenticates via the standard Google OAuth consent flow. Store the refresh token as GMAIL_OAUTH_REFRESH_TOKEN and the sending address as GMAIL_SENDER_ADDRESS in the credential store.
Required scopes
https://www.googleapis.com/auth/gmail.send
Webhook / trigger setup
No inbound webhook from Gmail is used. Gmail is a write destination only. The orchestration layer constructs the email payload and submits it via the Gmail API send endpoint.
Send endpoint
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send. The message body is a base64url-encoded RFC 2822 MIME message. The subject and HTML body are constructed from the template stored as GMAIL_CONFIRMATION_TEMPLATE in the credential store, with placeholders: {{client_name}}, {{job_date}}, {{arrival_window_start}}, {{arrival_window_end}}, {{crew_lead_name}}, {{crew_contact_number}}, {{job_title}}, {{site_address}}.
Required configuration
GMAIL_CONFIRMATION_TEMPLATE stores the full HTML email template. GMAIL_SENDER_ADDRESS stores the from address. GMAIL_REPLY_TO_ADDRESS stores the reply-to address (may differ from sender). All three must be populated in the credential store before go-live. The template must not contain any scheduling-specific data hardcoded into the template itself; all dynamic content must come from placeholders resolved at send time.
Rate limits
Gmail API enforces 1,000,000,000 quota units per day. A send call costs 100 units. At 40 to 80 confirmation emails per month, total usage is negligible. No throttling required.
Constraints
The Gmail API does not guarantee delivery confirmation beyond a successful HTTP 200 response. The message ID returned in the response must be logged against the job_id for audit purposes. If the client.email field is absent or malformed, the workflow must log the failure and alert via SLACK_CREW_CHANNEL_ID without blocking other workflow steps.

03Field mappings between tools

The tables below define the exact field mappings at each agent handoff. Source and destination field names are given in monospace as they appear in each tool's API response or request payload. All field names are case-sensitive.

Handoff 1: Jobber webhook payload to Availability and Assignment Agent (Agent 1)

Source tool
Source field
Destination tool
Destination field
Jobber
`data.job.id`
Orchestration layer
`job_id`
Jobber
`data.job.title`
Google Calendar / Slack / Gmail
`job_title`
Jobber
`data.job.client.name`
Google Calendar / Slack / Gmail
`client_name`
Jobber
`data.job.client.email`
Gmail / HubSpot lookup
`client_email`
Jobber
`data.job.client.phone`
Google Calendar description / Gmail
`client_phone`
Jobber
`data.job.property.address`
Google Calendar / Slack / Gmail
`site_address`
Jobber
`data.job.startAt`
Google Calendar / HubSpot / Slack / Gmail
`scheduled_start`
Jobber
`data.job.endAt`
Google Calendar / Gmail
`scheduled_end`
Jobber
`data.job.customFields.special_instructions`
Google Calendar description / Slack / Gmail
`special_instructions`

Handoff 2: Agent 1 output to Notifications and CRM Sync Agent (Agent 2)

Source tool
Source field
Destination tool
Destination field
Google Calendar (event response)
`id`
Orchestration layer audit log
`gcal_event_id`
Orchestration layer (assignment result)
`assigned_crew.name`
Slack / Gmail / HubSpot
`crew_lead_name`
Orchestration layer (assignment result)
`assigned_crew.email`
Slack user lookup / Jobber write-back
`crew_email`
Orchestration layer (assignment result)
`assigned_crew.phone`
Gmail template
`crew_contact_number`
Orchestration layer (assignment result)
`assigned_crew.calendar_id`
Google Calendar event attendees
`crew_calendar_id`
Orchestration layer (passed through)
`job_id`
Jobber PATCH / HubSpot / audit log
`job_id`
Orchestration layer (passed through)
`client_email`
HubSpot contact search
`client_email`
Orchestration layer (passed through)
`scheduled_start`
HubSpot deal property / Gmail
`scheduled_job_date`
Orchestration layer (passed through)
`site_address`
Slack message / Gmail
`site_address`
Orchestration layer (passed through)
`special_instructions`
Slack message / Gmail
`special_instructions`

Handoff 3: Agent 2 write-back to Jobber and HubSpot

Source tool
Source field
Destination tool
Destination field
Orchestration layer
`assigned_crew.employee_id`
Jobber PATCH body
`assigned_employee_ids`
Orchestration layer
`scheduled_start`
Jobber PATCH body
`scheduled_start`
Orchestration layer
`scheduled_end`
Jobber PATCH body
`scheduled_end`
Orchestration layer
`crew_lead_name`
HubSpot deal PATCH
`assigned_crew_member`
Orchestration layer
`scheduled_start`
HubSpot deal PATCH
`scheduled_job_date`
Orchestration layer
`job_id`
HubSpot deal PATCH
`job_id_reference`
Gmail API response
`id`
Orchestration layer audit log
`gmail_message_id`
Integration and API SpecPage 2 of 3
FS-DOC-05Technical

04Build stack and orchestration

Orchestration layout
Two discrete workflows are built in the automation platform: one per agent. Workflow 1 handles Agent 1 (Availability and Assignment). Workflow 2 handles Agent 2 (Notifications and CRM Sync). Workflow 1 passes its output payload to Workflow 2 via the platform's internal data-passing mechanism or a signed internal webhook. Both workflows share a single credential store. No credentials are duplicated across workflows.
Agent 1 trigger mechanism
Inbound webhook listener. Jobber sends a signed HTTP POST to the registered endpoint when a job status changes to confirmed. The orchestration layer validates the HMAC-SHA256 signature using JOBBER_WEBHOOK_SECRET before any processing begins. Invalid signatures result in an immediate HTTP 401 response and a logged alert. No polling is used for Agent 1.
Agent 2 trigger mechanism
Internal event or chained call from Workflow 1. Agent 2 is triggered programmatically by Workflow 1 upon successful completion of the crew assignment and calendar event creation steps. Agent 2 does not expose a public-facing webhook endpoint. There is no external polling for Agent 2.
Signature validation
Jobber webhook payloads include an X-Jobber-Hmac-SHA256 header. The orchestration layer computes HMAC-SHA256 over the raw request body using JOBBER_WEBHOOK_SECRET and compares to the header value using a constant-time comparison to prevent timing attacks. Mismatches are logged and the request is rejected before any workflow logic executes.
Idempotency
Each workflow execution is keyed on job_id + updated_at from the Jobber payload. If the same key is received within a 10-minute window, the duplicate is discarded and logged. This prevents double-processing caused by Jobber retrying unacknowledged webhooks.
Credential store
All secrets, tokens, IDs, and templates are stored in the automation platform's encrypted credential store. No credentials, template strings, stage IDs, or configuration values are hardcoded in workflow logic. See the credential store manifest below.
Credential store manifest: all secrets used across both agent workflows
// Credential store manifest
// All entries are encrypted at rest in the automation platform credential store.
// Rotate tokens marked [rotate 180d] every 180 days.

JOBBER_CLIENT_ID                  // Jobber OAuth app client ID
JOBBER_CLIENT_SECRET              // Jobber OAuth app client secret
JOBBER_ACCESS_TOKEN               // Current OAuth access token [auto-refresh]
JOBBER_REFRESH_TOKEN              // OAuth refresh token [rotate on use]
JOBBER_WEBHOOK_SECRET             // HMAC-SHA256 signing secret for inbound webhooks

GCAL_SERVICE_ACCOUNT_KEY          // Google Cloud service account JSON key (base64-encoded)
GCAL_CREW_CALENDAR_IDS            // JSON array: [{ name: string, calendar_id: string, zone: string, skills: string[] }]
GCAL_ASSIGNMENT_RULES             // JSON object: { priority_fields: ['zone','skill','availability'], fallback: 'exception' }

HUBSPOT_PRIVATE_APP_TOKEN         // HubSpot private app bearer token [rotate 180d]
HUBSPOT_ACTIVE_PIPELINE_STAGE_IDS // JSON array of active deal stage IDs from HubSpot pipeline
HUBSPOT_PROPERTY_NAMES            // JSON object: { scheduled_job_date: string, assigned_crew_member: string, job_id_reference: string }

SLACK_BOT_TOKEN                   // Slack bot OAuth token (xoxb-) [rotate 180d]
SLACK_CREW_CHANNEL_ID             // Fallback Slack channel ID for crew notifications
SLACK_JOB_NOTIFICATION_TEMPLATE   // Block Kit JSON template string with named placeholders

GMAIL_OAUTH_REFRESH_TOKEN         // Gmail API OAuth refresh token [rotate on reauth]
GMAIL_SENDER_ADDRESS              // Sending Gmail address (e.g. ops@[YourCompany.com])
GMAIL_REPLY_TO_ADDRESS            // Reply-to address for customer responses
GMAIL_CONFIRMATION_TEMPLATE       // Full HTML email template string with named placeholders
No credential, pipeline stage ID, Slack channel ID, HubSpot property name, or template string should ever appear as a literal value inside workflow logic. If a configuration value changes (e.g. a crew member leaves, a HubSpot property is renamed), update the credential store entry only. The workflow logic must remain unchanged.

05Error handling and retry logic

Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently. All errors are written to the workflow execution log with the job_id, timestamp, step name, HTTP status code or error message, and the action taken. Where a manual fallback is triggered, a Slack alert is sent to SLACK_CREW_CHANNEL_ID.

Integration
Scenario
Required behaviour
Jobber (inbound webhook)
Webhook signature validation fails
Reject immediately with HTTP 401. Log the raw header and job_id if present. Send a Slack alert to SLACK_CREW_CHANNEL_ID. Do not process the payload. No retry.
Jobber (inbound webhook)
Required fields missing from payload (e.g. site_address, scheduled_start)
Halt workflow execution at the validation step. Log the missing fields and job_id. Send a Slack alert listing the missing fields. No retry. Scheduler must manually confirm the job data in Jobber and resubmit.
Jobber (inbound webhook)
Duplicate payload received (same job_id + updated_at within 10 minutes)
Discard silently. Log the duplicate with job_id and received timestamp. Do not trigger downstream steps.
Google Calendar (freebusy query)
API returns HTTP 429 (rate limit) or 503 (service unavailable)
Retry with exponential backoff: 5s, 15s, 45s (3 attempts). If all retries fail, send a Slack alert to SLACK_CREW_CHANNEL_ID with the job_id and request the scheduler to assign manually. Log the failure.
Google Calendar (freebusy query)
No crew members available in the requested window
Do not auto-assign. Send a Slack alert to SLACK_CREW_CHANNEL_ID flagging the job_id, requested window, and 'no available crew found'. Route to scheduler for manual assignment. Halt Agent 1 at this step.
Google Calendar (event creation)
API returns HTTP 409 (conflict) or 400 (invalid field)
Retry once after 10 seconds. If the second attempt fails, log the error with the full request payload and send a Slack alert. Do not proceed to Agent 2 steps. Scheduler must create the event manually.
Jobber (write-back PATCH)
PATCH returns HTTP 422 (validation error) or 404 (job not found)
Log the error with job_id and full response body. Send a Slack alert. Continue to Agent 2 notification steps (do not block crew and customer notifications). Scheduler must update the Jobber record manually.
HubSpot (deal lookup or PATCH)
No matching deal found for client email, or PATCH returns 4xx
Log the miss with client_email and job_id. Send a Slack alert noting the CRM update was skipped. Continue to Slack and Gmail notification steps without blocking them. Do not retry automatically.
Slack (chat.postMessage)
API returns error or user lookup fails for crew email
Fall back to posting to SLACK_CREW_CHANNEL_ID with the crew member's name included in the message body. Log the fallback with the original crew_email. If the channel post also fails, retry once after 15 seconds. If both fail, send an email alert to GMAIL_SENDER_ADDRESS and log a critical error.
Gmail (message send)
API returns HTTP 401 (token expired) or 403 (scope denied)
Attempt token refresh using GMAIL_OAUTH_REFRESH_TOKEN. Retry the send once after refresh. If the refresh fails or the retry still returns an error, log the failure with client_email and job_id, and send a Slack alert to SLACK_CREW_CHANNEL_ID. Scheduler must send the confirmation manually.
Gmail (message send)
client_email is absent or malformed
Log the validation failure with job_id. Skip the Gmail step. Send a Slack alert noting the customer confirmation could not be sent and listing the job details. Do not halt other workflow steps.
Orchestration layer (any step)
Unhandled exception or unexpected error not matching the above scenarios
Catch all unhandled exceptions at the workflow level. Log the full error stack trace, job_id, step name, and timestamp. Send a Slack alert to SLACK_CREW_CHANNEL_ID with the error summary and job_id. Mark the workflow execution as failed. Do not retry unhandled exceptions automatically. FullSpec must be notified at support@gofullspec.com for any recurring unhandled exception pattern.
Unhandled exceptions must never fail silently. Every error path in this automation must produce at minimum: a written log entry with job_id and step name, and a Slack alert to SLACK_CREW_CHANNEL_ID. If the Slack alert itself cannot be delivered, a fallback email to GMAIL_SENDER_ADDRESS is the last-resort notification channel. Contact FullSpec at support@gofullspec.com for any error patterns that repeat across multiple job executions.
Integration and API SpecPage 3 of 3

More documents for this process

Every document generated for Job / Project 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