Back to Sales Forecasting

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

Sales Forecasting Automation

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

This document is the authoritative technical reference for every integration point in the Sales Forecasting automation. It covers the full tool inventory, per-tool authentication and configuration requirements, field mappings across agent handoffs, orchestration architecture, and defined error handling behaviour. The FullSpec team uses this specification to build, configure, and validate all connections. No credential or configuration detail should be hardcoded into workflow logic: everything sensitive belongs in the shared credential store described in Section 04.

01Tool inventory

Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
HubSpot
Live deal data source; stage-change webhook trigger
OAuth 2.0 / Private App token
Sales Hub Starter (API access)
Agent 1
Google Sheets
Forecast template store; approval trigger cell; output destination
OAuth 2.0 (Google service account)
Google Workspace (any paid tier)
Agent 2
Slack
Rep nudge delivery; pipeline snapshot post on distribution
OAuth 2.0 (Bot token)
Slack Pro or higher (bot scopes)
Agents 1, 3
Gmail
Stakeholder forecast email delivery
OAuth 2.0 (Google service account or user delegation)
Google Workspace (any paid tier)
Agent 3
Tableau
Dashboard data source refresh after forecast write
Personal Access Token (PAT)
Tableau Creator licence or Tableau Cloud
Agent 3
Orchestration layer
Workflow scheduling, trigger routing, credential store, retry logic, agent-to-agent handoff
Internal platform credentials
Platform subscription ($49/month)
All agents
Before you connect anything: provision sandbox or developer credentials for every tool listed above and validate each connection in a non-production environment before any production credentials are entered. HubSpot private app tokens, Google service account keys, Slack bot tokens, and Tableau PATs must all be tested against live-data-free environments first. Do not point any workflow step at a production pipeline, sheet, or Slack channel until end-to-end testing is complete.

02Per-tool integration detail

HubSpot

Primary data source for live deal records. Used by Agent 1 (Data Pull and Completeness Agent) on both a scheduled poll and a stage-change webhook. All deal data retrieved via the HubSpot Deals API v3.

Auth method
HubSpot Private App token (preferred over legacy API key). Generate under Settings > Integrations > Private Apps. Store token in the credential store as HUBSPOT_PRIVATE_APP_TOKEN.
Required scopes
crm.objects.deals.read, crm.objects.deals.write, crm.objects.contacts.read, crm.objects.owners.read, oauth, timeline
Webhook / trigger setup
Subscribe to crm.deal.propertyChange for the properties: dealstage, amount, closedate. Webhook endpoint is hosted by the orchestration layer. Validate using the X-HubSpot-Signature-v3 header: compute HMAC-SHA256 of the raw request body using the app secret stored as HUBSPOT_WEBHOOK_SECRET. Reject any request where the computed hash does not match the header value. Scheduled poll runs every Monday at 05:00 UTC as a fallback when no stage-change event fires.
Required configuration
Pipeline ID for the active sales pipeline must be stored in the credential store as HUBSPOT_PIPELINE_ID (do not hardcode). Each pipeline stage must have a numeric probability value (0-100) configured in HubSpot under Settings > Deals > Pipelines before Agent 2 can apply weights. Custom stage names must be mapped in the stage-probability lookup table held in Google Sheets (see Section 03).
Rate limits
HubSpot Deals API: 100 requests per 10 seconds, 250,000 requests per day (Sales Hub Starter). At current volume (approximately 18 runs/month, estimated 150-300 deal records per run), peak request count is well under 1,000 per cycle. Throttling is not required at current volume; implement exponential backoff only on HTTP 429 responses.
Hard constraints
Private App tokens do not expire but must be rotated if compromised. The API does not return soft-deleted deals by default: archived=false is the correct filter for open pipeline deals. Webhook payloads contain only the changed property and object ID: a follow-up GET /crm/v3/objects/deals/{id} call is required to retrieve full deal detail.
// Webhook payload (stage-change event)
POST /webhook/hubspot
{
  "subscriptionType": "deal.propertyChange",
  "objectId": 12345678,
  "propertyName": "dealstage",
  "propertyValue": "appointmentscheduled"
}

// Follow-up GET for full deal record
GET /crm/v3/objects/deals/{objectId}?properties=dealname,amount,closedate,dealstage,hubspot_owner_id,hs_lastmodifieddate

// Output to Agent 1 processing
deal_id, deal_name, amount, close_date, stage_id, owner_id, last_modified
Google Sheets

Hosts the structured forecast template written by Agent 2 (Forecast Calculation Agent) and the approval tab polled by Agent 3 (Distribution Agent). Also stores the stage-probability lookup table used by Agent 2.

Auth method
Google service account with domain-wide delegation. Service account JSON key stored in the credential store as GOOGLE_SERVICE_ACCOUNT_JSON. The service account must be granted Editor access to the forecast spreadsheet. Never use personal OAuth credentials for automated writes.
Required scopes
https://www.googleapis.com/auth/spreadsheets, https://www.googleapis.com/auth/drive.file
Webhook / trigger setup
Google Sheets has no native outbound webhook. Agent 3's approval trigger is implemented as a short-interval poll (every 2 minutes) against the approval cell in the Review tab. Poll uses the Sheets API GET /spreadsheets/{spreadsheetId}/values/{range} where range is Review!B2. When the cell value equals APPROVED (case-insensitive), Agent 3 proceeds. Spreadsheet ID stored as GSHEETS_FORECAST_SPREADSHEET_ID in the credential store.
Required configuration
The forecast spreadsheet must contain the following named tabs before deployment: Deals_Raw (Agent 2 writes deal-level rows), Projections_Weekly, Projections_Monthly, Historical_Benchmarks (read-only reference data), Stage_Probability_Lookup (stage_id to probability mapping), Review (contains approval cell at B2 and anomaly flags). Column headers in Deals_Raw must match exactly: deal_id, deal_name, amount, close_date, stage_id, stage_name, probability, weighted_value, owner_id, last_modified, stale_flag. Spreadsheet ID and all tab names are stored in the credential store: do not hardcode.
Rate limits
Google Sheets API: 300 read requests per minute per project, 300 write requests per minute per project. Agent 2 writes one batch request per forecast cycle covering all deal rows plus projection totals. At 18 runs/month the daily write count is under 10 batch operations. Throttling is not required at current volume.
Hard constraints
batchUpdate must be used for multi-row writes to stay within quota. valueInputOption must be set to RAW for numeric fields to prevent Sheets from reformatting currency or date values. Chart refresh in Sheets is automatic when underlying data ranges update: no separate chart API call is needed.
// Write payload to Deals_Raw tab
POST /spreadsheets/{spreadsheetId}/values/{range}:batchUpdate
{
  "valueInputOption": "RAW",
  "data": [
    {
      "range": "Deals_Raw!A2:K{n}",
      "values": [ [deal_id, deal_name, amount, close_date, stage_id, stage_name, probability, weighted_value, owner_id, last_modified, stale_flag], ... ]
    }
  ]
}

// Poll approval cell
GET /spreadsheets/{spreadsheetId}/values/Review!B2
// Expected response when approved:
{ "values": [["APPROVED"]] }
Slack

Used by Agent 1 to deliver targeted nudges to individual reps when their deal records are stale or incomplete, and by Agent 3 to post the pipeline snapshot to the configured channel after approval.

Auth method
Slack Bot OAuth token. Install app to workspace, grant bot scopes, and store the token in the credential store as SLACK_BOT_TOKEN. Do not use legacy webhook URLs.
Required scopes
chat:write, users:read, users:read.email, channels:read, im:write
Webhook / trigger setup
Slack is outbound-only for this process. No inbound webhook or event subscription is required. Agent 1 uses the chat.postMessage API to send direct messages to individual reps. Agent 3 uses chat.postMessage to post to the channel stored as SLACK_FORECAST_CHANNEL_ID. Rep Slack user IDs are resolved at runtime by calling users.lookupByEmail using the owner email returned by HubSpot. Resolved user IDs must be cached per run to avoid repeated lookups.
Required configuration
SLACK_FORECAST_CHANNEL_ID stored in the credential store (the channel where Agent 3 posts the pipeline snapshot). The nudge message template text is stored in the orchestration layer config, not hardcoded in the step. Nudge message must include: rep name, deal name, missing field description, and a direct link to the deal in HubSpot (constructed as https://app.hubspot.com/contacts/{portalId}/deal/{dealId}).
Rate limits
Slack Web API: Tier 3 methods (chat.postMessage) allow approximately 1 request per second. At current volume (estimated 5-15 nudge messages per cycle plus 1 channel post), the rate limit presents no constraint. No throttling required. Implement a 1-second delay between sequential postMessage calls as a precaution.
Hard constraints
The bot must be invited to the SLACK_FORECAST_CHANNEL_ID channel before Agent 3 can post. Direct messages to reps require im:write scope and the bot must have visibility of the target user. If a rep's HubSpot owner email does not resolve to a Slack user, log the failure and fall back to posting the nudge in the forecast channel with the rep's name mentioned as plain text.
// Agent 1: direct nudge to rep
POST https://slack.com/api/chat.postMessage
{
  "channel": "{slack_user_id}",
  "text": "Hi {rep_name}, your deal '{deal_name}' is missing {missing_field}. Please update it in HubSpot before the forecast runs: {deal_url}"
}

// Agent 3: channel post on approval
POST https://slack.com/api/chat.postMessage
{
  "channel": "{SLACK_FORECAST_CHANNEL_ID}",
  "text": "Pipeline snapshot: Weighted pipeline total {weighted_total}, {open_deal_count} open deals, forecast period {period}. Full forecast distributed via email."
}
Integration and API SpecPage 1 of 3
FS-DOC-05Technical
Gmail

Used exclusively by Agent 3 (Distribution Agent) to send the formatted forecast summary email to the leadership distribution list after the sales manager approves the forecast in Google Sheets.

Auth method
Google service account with domain-wide delegation, impersonating the sender address stored as GMAIL_SENDER_ADDRESS. Alternatively, a delegated user OAuth token is acceptable if domain-wide delegation is not available. Store credentials as GOOGLE_SERVICE_ACCOUNT_JSON (shared with Sheets) or GMAIL_OAUTH_TOKEN separately. The sending address must be authorised as a sender in the Google Workspace admin console.
Required scopes
https://www.googleapis.com/auth/gmail.send
Webhook / trigger setup
Gmail is outbound-only for this process. Agent 3 composes and sends a single email per approved forecast cycle using the Gmail API POST /users/me/messages/send. No inbound email polling or Gmail webhook is used.
Required configuration
GMAIL_SENDER_ADDRESS: the from address used for all forecast emails (stored in credential store). GMAIL_RECIPIENT_LIST: semicolon-separated list of leadership email addresses (stored in credential store, not hardcoded). Email subject line template stored in orchestration config: 'Sales Forecast: {period} | Approved {approval_timestamp}'. Email body uses an HTML template with placeholders: {weighted_total}, {open_deal_count}, {forecast_period}, {sheet_link}, {approval_timestamp}. Template is stored in the orchestration layer config.
Rate limits
Gmail API: 250 quota units per user per second; sending a single message costs 100 units. At one email per forecast cycle (approximately 18 runs/month), the daily send count is under 5. No throttling required.
Hard constraints
The Gmail API send endpoint requires the message body to be base64url-encoded RFC 2822 MIME format. Attachments (e.g. a PDF export of the sheet) must be encoded as MIME multipart if used in future; the current scope is send-only with an inline HTML body and a hyperlink to the Google Sheet. Email sending failures must trigger an alert to SLACK_FORECAST_CHANNEL_ID and must not silently drop the distribution.
// Agent 3: send forecast email
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
Authorization: Bearer {access_token}
{
  "raw": "{base64url_encoded_mime_message}"
}

// MIME message structure (before encoding)
From: {GMAIL_SENDER_ADDRESS}
To: {GMAIL_RECIPIENT_LIST}
Subject: Sales Forecast: {period} | Approved {approval_timestamp}
Content-Type: text/html; charset=UTF-8

<html><body>
  <p>Weighted pipeline: {weighted_total}</p>
  <p>Open deals: {open_deal_count} | Period: {forecast_period}</p>
  <a href="{sheet_link}">View full forecast in Google Sheets</a>
</body></html>
Tableau

Used by Agent 3 (Distribution Agent) to trigger a data source refresh so that Tableau dashboards reflect the newly written forecast data without any manual publish step. Depends on the Tableau licence tier in use.

Auth method
Tableau Personal Access Token (PAT). Generate under My Account Settings in Tableau Cloud or Tableau Server. Store as TABLEAU_PAT_NAME and TABLEAU_PAT_SECRET in the credential store. PATs expire after 15 days of inactivity: the orchestration layer must re-authenticate before each use using the POST /api/{api_version}/auth/signin endpoint.
Required scopes
Tableau REST API does not use OAuth scopes in the same manner. The PAT user must have the Site Role of Creator or Explorer (can publish) to trigger a datasource refresh. The specific data source must have the PAT user listed as a permitted connection owner or the site must allow all Creator-role users to refresh published sources.
Webhook / trigger setup
Tableau is outbound-only from the automation perspective. Agent 3 calls POST /api/{api_version}/sites/{siteId}/datasources/{datasourceId}/refresh to trigger the refresh. The datasource must be a published data source connected to the Google Sheets spreadsheet. TABLEAU_SITE_ID and TABLEAU_DATASOURCE_ID are stored in the credential store. On Tableau Cloud, the refresh is asynchronous: Agent 3 polls GET /api/{api_version}/sites/{siteId}/jobs/{jobId} at 10-second intervals up to a 5-minute timeout to confirm completion.
Required configuration
TABLEAU_SERVER_URL (e.g. https://10ay.online.tableau.com), TABLEAU_SITE_ID, TABLEAU_DATASOURCE_ID, TABLEAU_API_VERSION (e.g. 3.19), TABLEAU_PAT_NAME, TABLEAU_PAT_SECRET: all stored in the credential store. The published data source must be configured to use a Google Sheets live connection or extract. If the licence in use does not support API-triggered refreshes (e.g. Viewer licence only), step 9 remains a manual publish and this integration step must be skipped with an alert logged.
Rate limits
Tableau REST API: no published hard rate limit for refresh calls, but Tableau Cloud enforces a maximum of 1 concurrent extract refresh per data source. At 18 runs/month the single sequential refresh per cycle presents no constraint. No throttling required.
Hard constraints
If the Tableau PAT has been inactive for more than 15 days, authentication will fail. The orchestration layer must handle 401 responses by re-generating a session token using the stored PAT credentials before retrying. If the data source is not a published extract (i.e. it is a live connection to Sheets), no API refresh call is needed and Agent 3 skips this step. The distinction must be confirmed during the discovery and data audit phase and stored as TABLEAU_REFRESH_MODE (extract or live) in the orchestration config.
// Step 1: authenticate with PAT
POST {TABLEAU_SERVER_URL}/api/{TABLEAU_API_VERSION}/auth/signin
{
  "credentials": {
    "personalAccessTokenName": "{TABLEAU_PAT_NAME}",
    "personalAccessTokenSecret": "{TABLEAU_PAT_SECRET}",
    "site": { "contentUrl": "{TABLEAU_SITE_ID}" }
  }
}
// Response: { "credentials": { "token": "{auth_token}", "site": { "id": "{siteId}" } } }

// Step 2: trigger datasource refresh
POST {TABLEAU_SERVER_URL}/api/{TABLEAU_API_VERSION}/sites/{siteId}/datasources/{TABLEAU_DATASOURCE_ID}/refresh
X-Tableau-Auth: {auth_token}
// Response: { "job": { "id": "{jobId}" } }

// Step 3: poll job status
GET {TABLEAU_SERVER_URL}/api/{TABLEAU_API_VERSION}/sites/{siteId}/jobs/{jobId}
// Poll every 10 seconds; success when status == "Completed"

03Field mappings between tools

The tables below define the exact field mappings for each agent handoff. Source and destination field names are given in monospace as they appear in the respective API responses and sheet column headers. Any mismatch between these names and the actual tool configuration will cause write failures or silent data loss.

Handoff 1: HubSpot to Agent 1 processing (Data Pull and Completeness Agent)

Source tool
Source field
Destination tool
Destination field
HubSpot
`properties.hs_object_id`
Agent 1 payload
`deal_id`
HubSpot
`properties.dealname`
Agent 1 payload
`deal_name`
HubSpot
`properties.amount`
Agent 1 payload
`amount`
HubSpot
`properties.closedate`
Agent 1 payload
`close_date`
HubSpot
`properties.dealstage`
Agent 1 payload
`stage_id`
HubSpot
`properties.hubspot_owner_id`
Agent 1 payload
`owner_id`
HubSpot
`properties.hs_lastmodifieddate`
Agent 1 payload
`last_modified`
HubSpot
`properties.hubspot_owner_id` -> owners API -> `email`
Slack lookup
`owner_email`

Handoff 2: Agent 1 output to Google Sheets Deals_Raw tab (Forecast Calculation Agent)

Source tool
Source field
Destination tool
Destination field
Agent 1 payload
`deal_id`
Google Sheets
`Deals_Raw!A`
Agent 1 payload
`deal_name`
Google Sheets
`Deals_Raw!B`
Agent 1 payload
`amount`
Google Sheets
`Deals_Raw!C`
Agent 1 payload
`close_date`
Google Sheets
`Deals_Raw!D`
Agent 1 payload
`stage_id`
Google Sheets
`Deals_Raw!E`
Stage_Probability_Lookup
`stage_name` (resolved from `stage_id`)
Google Sheets
`Deals_Raw!F`
Stage_Probability_Lookup
`probability` (resolved from `stage_id`)
Google Sheets
`Deals_Raw!G`
Agent 2 calculated
`amount * probability / 100`
Google Sheets
`Deals_Raw!H` (`weighted_value`)
Agent 1 payload
`owner_id`
Google Sheets
`Deals_Raw!I`
Agent 1 payload
`last_modified`
Google Sheets
`Deals_Raw!J`
Agent 1 flag logic
`stale_flag` (boolean: amount null OR close_date > 30 days OR last_modified > 14 days)
Google Sheets
`Deals_Raw!K`

Handoff 3: Google Sheets projections to Gmail and Slack (Distribution Agent)

Source tool
Source field
Destination tool
Destination field
Google Sheets
`Projections_Weekly!B2` (weighted total, current week)
Gmail body
`{weighted_total}`
Google Sheets
`Projections_Monthly!B2` (weighted total, current month)
Gmail body
`{monthly_weighted_total}`
Google Sheets
`Deals_Raw` row count
Gmail body / Slack message
`{open_deal_count}`
Google Sheets
`Review!C2` (forecast period label)
Gmail subject / Slack message
`{period}`
Google Sheets
`Review!B3` (approval timestamp)
Gmail subject / Slack message
`{approval_timestamp}`
Google Sheets
Spreadsheet URL (constructed from GSHEETS_FORECAST_SPREADSHEET_ID)
Gmail body
`{sheet_link}`
Google Sheets
`Projections_Weekly!B2`
Slack message
`{weighted_total}`
All field names in the Deals_Raw tab are case-sensitive and must match exactly. If the Google Sheets template is restructured after deployment, the column index mapping in Agent 2's write step must be updated to match. Mismatched columns will cause silent overwrite of the wrong cells.
Integration and API SpecPage 2 of 3
FS-DOC-05Technical

04Build stack and orchestration

Orchestration layout
One workflow per agent: Workflow 1 (Data Pull and Completeness Agent), Workflow 2 (Forecast Calculation Agent), Workflow 3 (Distribution Agent). All three workflows share a single credential store. Agents pass data to each other via a shared internal state object or message queue within the orchestration platform: no external message broker is required at current volume.
Agent 1 trigger mechanism
Dual trigger: (1) Scheduled cron poll every Monday at 05:00 UTC. (2) Inbound HubSpot webhook on crm.deal.propertyChange events filtered to stage changes on deals above the configured value threshold. Webhook signature validated using HMAC-SHA256 against HUBSPOT_WEBHOOK_SECRET before any processing begins. Both triggers route to the same Workflow 1 entry point.
Agent 2 trigger mechanism
Event-based: Agent 2 (Workflow 2) is triggered by a success completion signal emitted by Agent 1 (Workflow 1) upon writing the completion flag to a designated internal variable. No external poll or webhook: the orchestration layer handles the handoff natively via an internal trigger on Workflow 1 completion status.
Agent 3 trigger mechanism
Poll-based: Agent 3 (Workflow 3) polls the Google Sheets approval cell (Review!B2) every 2 minutes using the Sheets API. When the cell value equals APPROVED (case-insensitive match), Workflow 3 proceeds to distribution. Poll is initiated as a sub-loop within Workflow 3 with a configurable timeout of 48 hours: after timeout, an alert is posted to SLACK_FORECAST_CHANNEL_ID and the cycle is marked as pending-expired. Slack and Gmail steps execute sequentially; Tableau refresh executes after both messaging steps succeed.
Credential store type
The orchestration platform's native encrypted credential/secret store. No credentials are stored in plain text in workflow nodes, environment files, or sheet cells.
Retry configuration
All HTTP steps are configured with a maximum of 3 retry attempts using exponential backoff (delays: 30 seconds, 90 seconds, 270 seconds). Steps that exceed max retries emit an error event routed to the error-handling sub-workflow (see Section 05).
Credential store: all secrets are injected by the orchestration platform at runtime. Rotate tokens and update this store immediately if any credential is compromised.
// Credential store contents (all values injected at runtime; never hardcoded in workflow nodes)

HUBSPOT_PRIVATE_APP_TOKEN        = "pat-na1-xxxxxxxxxxxxxxxxxxxx"
HUBSPOT_WEBHOOK_SECRET           = "whsec_xxxxxxxxxxxxxxxxxxxx"
HUBSPOT_PIPELINE_ID              = "1234567"
HUBSPOT_PORTAL_ID                = "9876543"
HUBSPOT_DEAL_VALUE_THRESHOLD     = 5000        // minimum deal value (USD) to trigger stage-change webhook

GOOGLE_SERVICE_ACCOUNT_JSON      = "{...}"   // full service account key JSON, stored as secret
GSHEETS_FORECAST_SPREADSHEET_ID  = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms"

SLACK_BOT_TOKEN                  = "xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx"
SLACK_FORECAST_CHANNEL_ID        = "C0123456789"

GMAIL_SENDER_ADDRESS             = "forecasts@[YourCompany.com]"
GMAIL_RECIPIENT_LIST             = "ceo@[YourCompany.com];vpsales@[YourCompany.com];cfo@[YourCompany.com]"

TABLEAU_SERVER_URL               = "https://10ay.online.tableau.com"
TABLEAU_SITE_ID                  = "yourcompany"
TABLEAU_DATASOURCE_ID            = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
TABLEAU_API_VERSION              = "3.19"
TABLEAU_PAT_NAME                 = "ForecastAutomationPAT"
TABLEAU_PAT_SECRET               = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
TABLEAU_REFRESH_MODE             = "extract"  // or "live": set during discovery phase

FORECAST_STALE_DAYS              = 14         // days since last_modified before stale_flag = true
FORECAST_CLOSEDATE_THRESHOLD     = 30         // days since close_date before flagged as overdue
APPROVAL_POLL_INTERVAL_SECONDS   = 120
APPROVAL_TIMEOUT_HOURS           = 48

05Error handling and retry logic

Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently: all error paths must either retry with backoff, post an alert to SLACK_FORECAST_CHANNEL_ID, or both. The table below defines the required behaviour for each scenario. The error-handling sub-workflow receives error events from all three agent workflows and is responsible for alert dispatch.

Integration
Scenario
Required behaviour
HubSpot API
HTTP 429 (rate limit exceeded)
Pause execution; apply exponential backoff (30s, 90s, 270s); retry up to 3 times. If still failing after 3 retries, post alert to SLACK_FORECAST_CHANNEL_ID and halt Workflow 1 for the current cycle. Log full response body.
HubSpot API
HTTP 401 (invalid or expired token)
Do not retry automatically. Post alert: 'HubSpot token invalid. Update HUBSPOT_PRIVATE_APP_TOKEN in credential store.' Halt Workflow 1. Manual intervention required.
HubSpot Webhook
Signature validation failure (X-HubSpot-Signature-v3 mismatch)
Reject request immediately with HTTP 401 response. Log the source IP and payload hash. Post alert to SLACK_FORECAST_CHANNEL_ID. Do not process payload. No retry.
HubSpot API
Deal record returns null amount or missing required field
Flag deal as incomplete (stale_flag = true). Include in Slack nudge to owning rep. Continue processing remaining deals: do not halt the cycle for a single incomplete record.
Google Sheets API
HTTP 403 (service account lacks permission)
Do not retry. Post alert: 'Sheets write failed: service account permission denied on spreadsheet {GSHEETS_FORECAST_SPREADSHEET_ID}.' Halt Workflow 2. Manual intervention required to restore access.
Google Sheets API
HTTP 429 (quota exceeded during batchUpdate)
Apply exponential backoff (30s, 90s, 270s); retry up to 3 times. If still failing, post alert and halt Workflow 2 for the current cycle. Log quota usage details.
Google Sheets poll
Approval timeout: Review!B2 not set to APPROVED within 48 hours
Post alert to SLACK_FORECAST_CHANNEL_ID: 'Forecast approval overdue for cycle {period}. Please review and approve in Google Sheets.' Mark cycle as pending-expired in the orchestration log. Do not distribute.
Slack API
HTTP 429 (message rate limit)
Wait 1 second between sequential postMessage calls (standard precaution). On 429: apply backoff (10s, 30s, 60s); retry up to 3 times. If rep nudge fails after retries, log failure and post a fallback nudge in SLACK_FORECAST_CHANNEL_ID mentioning the rep's name.
Slack API
users.lookupByEmail returns no match for a HubSpot owner email
Log the unmatched email. Post nudge to SLACK_FORECAST_CHANNEL_ID as fallback: '@here {rep_name} ({owner_email}) has stale deal records. Please update before the forecast closes.' Continue processing other reps.
Gmail API
Send failure (HTTP 5xx or quota error)
Retry with backoff (30s, 90s, 270s) up to 3 times. If all retries fail, post alert to SLACK_FORECAST_CHANNEL_ID: 'Forecast email distribution failed for cycle {period}. Manual send required.' Attach the sheet link to the Slack alert. Do not silently drop the distribution.
Tableau API
HTTP 401 (PAT expired or inactive)
Attempt re-authentication using stored TABLEAU_PAT_NAME and TABLEAU_PAT_SECRET. If re-auth succeeds, retry the refresh call once. If re-auth fails, post alert: 'Tableau PAT expired. Regenerate and update TABLEAU_PAT_SECRET in credential store.' Log and continue: Gmail and Slack distribution must not be blocked by a Tableau failure.
Tableau API
Refresh job times out (job status not Completed within 5 minutes)
Log the job ID and post a non-blocking alert to SLACK_FORECAST_CHANNEL_ID: 'Tableau refresh job {jobId} timed out. Dashboard may not reflect latest forecast. Check Tableau job queue.' Do not halt distribution steps.
All unhandled exceptions and uncaught errors at any workflow step must be routed to the error-handling sub-workflow and must result in an alert to SLACK_FORECAST_CHANNEL_ID. Silent failures are not acceptable. Every error event must include: the workflow name, the step name, the timestamp, the HTTP status code or exception type, and a plain-English description of the required manual action if one is needed. Contact the FullSpec team at support@gofullspec.com if an error pattern persists across multiple cycles.
Integration and API SpecPage 3 of 3

More documents for this process

Every document generated for Sales Forecasting.

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