FS-DOC-05Technical
Integration and API Spec
Business KPI Dashboard
[YourCompany.com] · Operations Department · Prepared by FullSpec · [Today's Date]
This document is the definitive technical reference for every integration point in the Business KPI Dashboard automation. It covers authentication requirements, exact API scopes, webhook configuration, field-level data mappings, credential storage conventions, and error handling behaviour for all six connected tools. The FullSpec team uses this specification during build and testing; it is the source of truth for any future changes to the integration layer. No credentials should ever be hardcoded in workflow steps: all secrets are stored exclusively in the shared credential store referenced in Section 04.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
Orchestration layer
Hosts all workflows, triggers, credential store, and inter-agent handoffs
N/A (internal)
N/A
All
Xero
Source of financial metrics: revenue, outstanding invoices, cash position
OAuth 2.0 (PKCE)
Starter ($29/mo) or above; API access on all plans
Agent 1
HubSpot
Source of sales pipeline metrics: deal count, pipeline value, conversion rate
OAuth 2.0 (Private App token)
Free CRM or above; API access included
Agent 1
Google Sheets
Central KPI calculation store; input cells written by Agent 1; read by Agent 2
OAuth 2.0 (service account)
Google Workspace or personal Google account (free)
Agent 1, Agent 2
Notion
Commentary publication target and reporting archive
OAuth 2.0 (Notion integration token)
Free plan sufficient; sharing must be enabled on the target page
Agent 2
Slack
Automated report distribution to team channel
OAuth 2.0 (Bot token, scopes below)
Free plan sufficient for bot posting
Agent 3
Google Looker Studio
Live visual dashboard linked in the Slack message
Google account (shared link, no API write required)
Free; dashboard must be set to 'Anyone with the link can view'
Agent 3
Before you connect anything: provision sandbox or test credentials for every tool listed above and validate each connection end-to-end in a non-production environment before any production credentials are entered into the credential store. Do not use live Xero or HubSpot data during connection testing.
02Per-tool integration detail
Xero
Read-only financial data source. The Data Collection Agent (Agent 1) calls the Xero Accounting API on schedule to retrieve revenue, invoice, and cash position figures for the current reporting period.
Auth method
OAuth 2.0 with PKCE. Obtain a Client ID and Client Secret from the Xero Developer Portal (app.xero.com). The token endpoint is https://identity.xero.com/connect/token. Access tokens expire after 30 minutes; the platform must handle automatic token refresh using the stored refresh token.
Required scopes
openid profile email accounting.transactions.read accounting.reports.read accounting.settings.read
Webhook / trigger setup
No inbound webhook required. Agent 1 polls on schedule (Monday 07:00 local time). Xero webhooks are not used in this build.
Required configuration
XERO_CLIENT_ID and XERO_CLIENT_SECRET stored in credential store. XERO_TENANT_ID (the organisation identifier, retrieved from GET /connections after OAuth handshake) stored in credential store. Reporting period window calculated dynamically as the 7-day range ending at trigger time. No tenant ID should appear hardcoded in any workflow step.
Rate limits
Xero enforces 60 API calls/minute per app and 5,000 calls/day per app. At current volume (4 reporting cycles/month, 3 API calls per cycle: GET /Reports/ProfitAndLoss, GET /Invoices, GET /BankSummary), total monthly calls are approximately 12. Throttling is not required at this volume. Build a 1-second delay between sequential calls as a defensive measure.
Constraints
Read-only scopes only. Never request accounting.transactions or accounting.attachments.read beyond what is listed above. Token refresh must be handled automatically; manual re-auth breaks the weekly schedule. The Xero app must be set to 'Web app' type in the developer portal, not 'Machine to machine'.
// Xero API calls made by Agent 1
GET /api.xro/2.0/Reports/ProfitAndLoss?fromDate={period_start}&toDate={period_end}
GET /api.xro/2.0/Invoices?where=Status=="AUTHORISED"&DateFrom={period_start}&DateTo={period_end}
GET /api.xro/2.0/Reports/BankSummary?fromDate={period_start}&toDate={period_end}
// Key fields extracted
response.Reports[0].Rows -> total_revenue
response.Invoices -> outstanding_invoice_count, outstanding_invoice_total
response.Reports[0].Rows -> closing_cash_balanceHubSpot
Read-only sales pipeline data source. Agent 1 calls the HubSpot CRM API to retrieve deal count, total pipeline value, and conversion rate for the reporting period.
Auth method
Private App token (Bearer token). Create a Private App in HubSpot Settings > Integrations > Private Apps. Copy the generated token into the credential store. No OAuth redirect flow is required for Private Apps.
Required scopes
crm.objects.deals.read crm.objects.contacts.read crm.schemas.deals.read sales-email-read
Webhook / trigger setup
No inbound webhook required. Agent 1 polls HubSpot on schedule immediately after the Xero call completes. HubSpot webhooks are not used in this build.
Required configuration
HUBSPOT_PRIVATE_APP_TOKEN stored in credential store. HUBSPOT_PIPELINE_ID stored in credential store (the specific pipeline to report on; retrieve from GET /crm/v3/pipelines/deals and store the id value, not the label). HUBSPOT_REPORTING_STAGE_IDS stored as a JSON array of stage IDs representing 'active' deals for pipeline value calculation. Date filter uses the same period_start and period_end as the Xero call.
Rate limits
HubSpot Private Apps allow 110 requests/10 seconds and 250,000 requests/day. At current volume (4 cycles/month, 2 API calls per cycle: POST /crm/v3/objects/deals/search twice), total monthly calls are approximately 8. Throttling is not required. A 500ms inter-call delay is sufficient.
Constraints
Do not request marketing, content, or account scopes. The pipeline ID and stage IDs must be stored in the credential store, not hardcoded; pipeline configurations change over time. If pagination is returned (deals > 100 in a period), the workflow must follow the paging.next.link cursor until all records are retrieved before aggregating totals.
// HubSpot API calls made by Agent 1
POST /crm/v3/objects/deals/search
body: { filterGroups: [{ filters: [
{ propertyName: 'pipeline', operator: 'EQ', value: HUBSPOT_PIPELINE_ID },
{ propertyName: 'closedate', operator: 'BETWEEN', value: period_start, highValue: period_end }
]}], properties: ['dealname','amount','dealstage','hs_deal_stage_probability'] }
// Key fields extracted
response.results -> deal_count
SUM(response.results[].amount WHERE dealstage IN HUBSPOT_REPORTING_STAGE_IDS) -> pipeline_value
AVG(response.results[].hs_deal_stage_probability) -> avg_conversion_rateGoogle Sheets
Central KPI store. Agent 1 writes raw metric values into designated input cells. Existing spreadsheet formulas calculate derived KPIs (margin, growth rate, etc.). Agent 2 reads the calculated output cells to generate commentary.
Auth method
OAuth 2.0 via a Google Cloud service account. Create a service account in Google Cloud Console, download the JSON key file, and store the key contents in the credential store as GOOGLE_SA_KEY_JSON. Share the target spreadsheet with the service account email address (editor access for Agent 1; viewer access is sufficient for Agent 2 but editor is acceptable for simplicity).
Required scopes
https://www.googleapis.com/auth/spreadsheets https://www.googleapis.com/auth/drive.file
Webhook / trigger setup
No inbound webhook. Agent 2 is triggered by a success signal from Agent 1 via the orchestration layer's inter-workflow handoff mechanism, not by a Sheets webhook.
Required configuration
GOOGLE_SPREADSHEET_ID stored in credential store (the long alphanumeric ID from the Sheets URL). SHEETS_INPUT_RANGE stored in credential store (e.g. 'KPI_Input'!B2:B12 — the named range containing all raw input cells). SHEETS_OUTPUT_RANGE stored in credential store (e.g. 'KPI_Calculated'!B2:B20 — the range Agent 2 reads). The sheet tab names and cell structure must not be changed after go-live without updating the credential store entries.
Rate limits
Google Sheets API allows 300 requests/minute per project and 60 requests/minute per user. At current volume (4 writes/month from Agent 1, 4 reads/month from Agent 2), total monthly calls are approximately 8. Throttling is not required.
Constraints
Batch all metric writes into a single batchUpdate call rather than one call per cell to minimise quota usage and ensure atomicity. If the batchUpdate fails mid-write, no partial data should be committed: use a staging approach where all values are validated before the batch is submitted.
// Agent 1 write — single batchUpdate call
PUT /v4/spreadsheets/{GOOGLE_SPREADSHEET_ID}/values/{SHEETS_INPUT_RANGE}:batchUpdate
body: { valueInputOption: 'USER_ENTERED', data: [
{ range: 'KPI_Input!B2', values: [[total_revenue]] },
{ range: 'KPI_Input!B3', values: [[outstanding_invoice_total]] },
{ range: 'KPI_Input!B4', values: [[closing_cash_balance]] },
{ range: 'KPI_Input!B5', values: [[deal_count]] },
{ range: 'KPI_Input!B6', values: [[pipeline_value]] },
{ range: 'KPI_Input!B7', values: [[avg_conversion_rate]] }
]}
// Agent 2 read
GET /v4/spreadsheets/{GOOGLE_SPREADSHEET_ID}/values/{SHEETS_OUTPUT_RANGE}
-> returns calculated KPI rows for commentary generationIntegration and API SpecPage 1 of 3
FS-DOC-05Technical
Notion
Commentary publication target and reporting archive. Agent 2 writes the AI-generated KPI commentary to a designated Notion page for the current reporting week.
Auth method
Notion Integration Token (Bearer token). Create an internal integration at https://www.notion.so/my-integrations. Copy the 'Internal Integration Token' into the credential store as NOTION_TOKEN. Share the target Notion page with the integration (use 'Add connections' on the page, not workspace-level sharing).
Required scopes
Notion integration scopes are set at creation time, not per-call. Enable: Read content, Update content, Insert content. Do not enable User information or Comment scopes unless required by a future feature.
Webhook / trigger setup
No inbound webhook. Agent 2 creates or updates a Notion page via the Blocks API after the AI commentary text is generated. The page is identified by NOTION_REPORT_PAGE_ID stored in the credential store.
Required configuration
NOTION_TOKEN stored in credential store. NOTION_DATABASE_ID stored in credential store (the ID of the Notion database used as the reporting archive, retrieved from the database URL). Each weekly report is created as a new child page with a title following the pattern 'KPI Report - YYYY-WW'. NOTION_PARENT_PAGE_ID stored in credential store for page creation. No page IDs should be hardcoded in workflow steps.
Rate limits
Notion API enforces 3 requests/second per integration. At current volume (4 page-create calls/month), throttling is not required. If block-level writes are broken into multiple append calls, add a 400ms delay between each.
Constraints
The Notion API uses block-level content: plain text commentary must be structured as paragraph blocks in the request body. Rich text formatting (bold for KPI names, normal for narrative) should be applied via the rich_text array. Page titles must be unique within the database to prevent duplicate archive entries; include the ISO week number in the title.
// Agent 2 creates a new Notion page
POST /v1/pages
headers: { Authorization: Bearer NOTION_TOKEN, Notion-Version: '2022-06-28' }
body: {
parent: { database_id: NOTION_DATABASE_ID },
properties: { Name: { title: [{ text: { content: 'KPI Report - {YYYY-WW}' }}]}},
children: [
{ object: 'block', type: 'paragraph',
paragraph: { rich_text: [{ type: 'text', text: { content: ai_commentary_text }}]}}
]
}
// Response
response.id -> notion_page_id (passed to Agent 3 as handoff payload)Slack
Automated report distribution. Agent 3 posts a formatted message to the configured team channel containing headline KPI figures, the AI commentary summary, and the Looker Studio dashboard link.
Auth method
OAuth 2.0 Bot token. Create a Slack App at api.slack.com/apps, add it to the workspace, and install it to obtain the Bot User OAuth Token. Store as SLACK_BOT_TOKEN in the credential store.
Required scopes
chat:write chat:write.public channels:read users:read (users:read required only if @mentions of stakeholders are configured)
Webhook / trigger setup
No inbound webhook required. Agent 3 calls the Slack Web API (POST /api/chat.postMessage) after receiving the Notion page confirmation from Agent 2. Slack Incoming Webhooks are not used; the Bot token approach is preferred for its ability to read channel membership and thread replies if needed later.
Required configuration
SLACK_BOT_TOKEN stored in credential store. SLACK_CHANNEL_ID stored in credential store (use the channel ID, not the display name, to prevent breakage if the channel is renamed). SLACK_STAKEHOLDER_USER_IDS stored as a JSON array of Slack user IDs to @mention (e.g. ["U012AB3CD", "U098ZY7WX"]). LOOKER_STUDIO_DASHBOARD_URL stored in credential store. No channel names or user IDs should be hardcoded in workflow steps.
Rate limits
Slack Web API Tier 3 methods (chat.postMessage) allow 50 requests/minute. At current volume (4 posts/month), throttling is not required.
Constraints
The Slack app must be invited to the target channel before it can post (use /invite @appname in the channel). Use Block Kit formatting for the message body to ensure consistent rendering. Keep the fallback text field populated for accessibility. Do not post the Looker Studio URL as a bare link; wrap it in a button action block.
// Agent 3 posts to Slack
POST /api/chat.postMessage
headers: { Authorization: Bearer SLACK_BOT_TOKEN }
body: {
channel: SLACK_CHANNEL_ID,
text: 'KPI Dashboard for week {YYYY-WW} is live.',
blocks: [
{ type: 'header', text: { type: 'plain_text', text: 'Weekly KPI Report - {YYYY-WW}' }},
{ type: 'section', text: { type: 'mrkdwn', text: ai_commentary_summary }},
{ type: 'section', fields: [
{ type: 'mrkdwn', text: '*Revenue:* {total_revenue}' },
{ type: 'mrkdwn', text: '*Pipeline:* {pipeline_value}' },
{ type: 'mrkdwn', text: '*Deals:* {deal_count}' },
{ type: 'mrkdwn', text: '*Conversion:* {avg_conversion_rate}%' }
]},
{ type: 'actions', elements: [
{ type: 'button', text: { type: 'plain_text', text: 'View Dashboard' },
url: LOOKER_STUDIO_DASHBOARD_URL }
]}
]
}Google Looker Studio
Live visual dashboard shared via link in the Slack message. No API write is required; Looker Studio connects directly to the Google Sheet as its data source and refreshes automatically when the sheet is updated.
Auth method
Google account authentication within Looker Studio (managed by Google, not by the orchestration layer). The dashboard must be published with sharing set to 'Anyone with the link can view'. No API credentials are stored for Looker Studio in the credential store.
Required scopes
No programmatic API scopes required. Looker Studio reads from Google Sheets using the data source connection configured inside the Looker Studio UI by the FullSpec team during build. The service account used for Sheets (GOOGLE_SA_KEY_JSON) must be granted viewer access to the Sheets data source within Looker Studio.
Webhook / trigger setup
No webhook. Looker Studio polls the connected Google Sheet on a schedule defined within the data source settings. Set the data freshness to 1 hour or less to ensure the dashboard reflects Agent 1's writes within the reporting window.
Required configuration
LOOKER_STUDIO_DASHBOARD_URL stored in credential store (the public share link). The Sheets data source within Looker Studio must reference GOOGLE_SPREADSHEET_ID and the 'KPI_Calculated' tab. If the dashboard is ever regenerated or the share link changes, LOOKER_STUDIO_DASHBOARD_URL must be updated in the credential store and the Slack message template will reflect the change automatically.
Rate limits
No API rate limits applicable. Looker Studio data refresh is governed by Google's internal scheduler. No throttling configuration required.
Constraints
The Looker Studio dashboard share link must remain active and public. If the sharing setting is revoked or the dashboard is moved, Agent 3's Slack message will contain a broken link. The FullSpec team will verify the link is live during each end-to-end test cycle.
// No API call made by the automation platform
// Looker Studio refreshes automatically from Google Sheets data source
// Agent 3 uses the stored URL only
LOOKER_STUDIO_DASHBOARD_URL -> embedded in Slack Block Kit button action
// Validation step in Agent 3
HTTP GET {LOOKER_STUDIO_DASHBOARD_URL} -> assert HTTP 200 before posting to Slack
on non-200: halt Agent 3, notify via SLACK_ALERT_CHANNEL_ID, log error03Field mappings between tools
The tables below document every field-level mapping across the three agent handoff boundaries. Source field names use exact API response paths or spreadsheet range references. Destination field names use exact API request paths or credential store keys.
Agent 1 handoff: Xero and HubSpot to Google Sheets
Source tool
Source field (exact)
Destination tool
Destination field (exact)
Xero
Reports[0].Rows[?(@.RowType=='Section')].Rows[?(@.Cells[0].Value=='Total Income')].Cells[1].Value
Google Sheets
KPI_Input!B2
Xero
SUM(Invoices[?(@.Status=='AUTHORISED')].AmountDue)
Google Sheets
KPI_Input!B3
Xero
Reports[0].Rows[?(@.RowType=='Row' && @.Cells[0].Value=='Closing Balance')].Cells[1].Value
Google Sheets
KPI_Input!B4
HubSpot
COUNT(results[]) WHERE dealstage IN HUBSPOT_REPORTING_STAGE_IDS
Google Sheets
KPI_Input!B5
HubSpot
SUM(results[].properties.amount) WHERE dealstage IN HUBSPOT_REPORTING_STAGE_IDS
Google Sheets
KPI_Input!B6
HubSpot
AVG(results[].properties.hs_deal_stage_probability)
Google Sheets
KPI_Input!B7
Xero
period_start (ISO 8601 string, computed by trigger)
Google Sheets
KPI_Input!B1 (period label cell)
HubSpot
period_end (ISO 8601 string, computed by trigger)
Google Sheets
KPI_Input!B1 (same period label cell, formatted as range string)
Agent 2 handoff: Google Sheets to Notion
Source tool
Source field (exact)
Destination tool
Destination field (exact)
Google Sheets
KPI_Calculated!B2 (calculated total revenue)
Notion
page.properties.Revenue.rich_text[0].text.content
Google Sheets
KPI_Calculated!B3 (calculated gross margin %)
Notion
page.properties.GrossMargin.rich_text[0].text.content
Google Sheets
KPI_Calculated!B4 (outstanding invoices total)
Notion
page.properties.OutstandingInvoices.rich_text[0].text.content
Google Sheets
KPI_Calculated!B5 (closing cash balance)
Notion
page.properties.CashBalance.rich_text[0].text.content
Google Sheets
KPI_Calculated!B6 (deal count)
Notion
page.properties.DealCount.rich_text[0].text.content
Google Sheets
KPI_Calculated!B7 (pipeline value)
Notion
page.properties.PipelineValue.rich_text[0].text.content
Google Sheets
KPI_Calculated!B8 (avg conversion rate %)
Notion
page.properties.ConversionRate.rich_text[0].text.content
Google Sheets
KPI_Calculated!B9 (week-on-week revenue delta %)
Notion
page.children[0].paragraph.rich_text[0].text.content (AI commentary block)
AI layer (internal)
ai_commentary_text (generated string)
Notion
page.children[1].paragraph.rich_text[0].text.content
Agent 3 handoff: Notion and Google Sheets to Slack
Source tool
Source field (exact)
Destination tool
Destination field (exact)
Notion
response.id (page ID returned on creation)
Slack
blocks[1].text.text (used to construct Notion page URL in message body)
Google Sheets
KPI_Calculated!B2 (total revenue)
Slack
blocks[2].fields[0].text
Google Sheets
KPI_Calculated!B7 (pipeline value)
Slack
blocks[2].fields[1].text
Google Sheets
KPI_Calculated!B6 (deal count)
Slack
blocks[2].fields[2].text
Google Sheets
KPI_Calculated!B8 (conversion rate %)
Slack
blocks[2].fields[3].text
AI layer (internal)
ai_commentary_summary (truncated to 280 chars)
Slack
blocks[1].text.text
Credential store
LOOKER_STUDIO_DASHBOARD_URL
Slack
blocks[3].elements[0].url
Integration and API SpecPage 2 of 3
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Three discrete workflows, one per agent: Workflow 1 (Data Collection Agent), Workflow 2 (KPI Commentary Agent), Workflow 3 (Distribution Agent). All three workflows share a single credential store. Workflow 2 is triggered by a success event emitted by Workflow 1; Workflow 3 is triggered by a success event emitted by Workflow 2. No workflow polls another workflow's status by timer: handoffs are event-driven.
Agent 1 trigger mechanism
Time-based schedule (cron). Fires every Monday at 07:00 in the configured local timezone. Cron expression: 0 7 * * 1. No webhook; no signature validation required. The trigger passes period_start and period_end as computed variables to the workflow context.
Agent 2 trigger mechanism
Webhook (internal). Workflow 1 emits a success payload to Workflow 2's internal webhook endpoint upon successful batchUpdate to Google Sheets. The payload includes: { run_id, period_start, period_end, sheets_write_status: 'SUCCESS', anomaly_flag: true|false }. Signature validation: the orchestration platform signs the internal webhook payload with HMAC-SHA256 using INTERNAL_WEBHOOK_SECRET. Workflow 2 must validate the signature header before processing.
Agent 3 trigger mechanism
Webhook (internal). Workflow 2 emits a success payload to Workflow 3's internal webhook endpoint upon successful Notion page creation. The payload includes: { run_id, period_start, period_end, notion_page_id, ai_commentary_summary }. Same HMAC-SHA256 signature validation using INTERNAL_WEBHOOK_SECRET applies.
Anomaly flag routing
If Workflow 1 sets anomaly_flag: true in its handoff payload, Workflow 2 pauses commentary generation and posts an alert to SLACK_ALERT_CHANNEL_ID, tagging the ops manager user ID from SLACK_STAKEHOLDER_USER_IDS[0]. The ops manager reviews the flagged figure in Google Sheets and manually triggers Workflow 2 resume via a Slack slash command or by clicking an approval link in the alert message.
Credential store type
The automation platform's built-in encrypted credential/secret store. All secrets are referenced by environment variable name within workflow steps. No credentials appear in workflow step configurations, logs, or exported workflow definitions.
Credential store contents (all entries required before build begins):
All values are stored in the platform credential store. No value from this list should appear anywhere in a workflow step, log output, or exported configuration file.
# Xero
XERO_CLIENT_ID = '<from Xero Developer Portal>'
XERO_CLIENT_SECRET = '<from Xero Developer Portal>'
XERO_TENANT_ID = '<retrieved from GET /connections after OAuth handshake>'
XERO_REFRESH_TOKEN = '<stored after initial OAuth; auto-rotated on each refresh>'
# HubSpot
HUBSPOT_PRIVATE_APP_TOKEN = '<from HubSpot Private App settings>'
HUBSPOT_PIPELINE_ID = '<retrieved from GET /crm/v3/pipelines/deals>'
HUBSPOT_REPORTING_STAGE_IDS = '["stage_id_1", "stage_id_2"]'
# Google
GOOGLE_SA_KEY_JSON = '<full JSON contents of service account key file>'
GOOGLE_SPREADSHEET_ID = '<alphanumeric ID from Google Sheets URL>'
SHEETS_INPUT_RANGE = 'KPI_Input!B1:B7'
SHEETS_OUTPUT_RANGE = 'KPI_Calculated!B2:B9'
# Notion
NOTION_TOKEN = '<Internal Integration Token from notion.so/my-integrations>'
NOTION_DATABASE_ID = '<alphanumeric ID from Notion database URL>'
NOTION_PARENT_PAGE_ID = '<ID of parent page under which weekly reports are created>'
# Slack
SLACK_BOT_TOKEN = '<Bot User OAuth Token from api.slack.com/apps>'
SLACK_CHANNEL_ID = '<target channel ID, not display name>'
SLACK_ALERT_CHANNEL_ID = '<ops alert channel ID for anomaly and error notifications>'
SLACK_STAKEHOLDER_USER_IDS = '["U012AB3CD", "U098ZY7WX"]'
# Looker Studio
LOOKER_STUDIO_DASHBOARD_URL = '<public share link, verified accessible before each run>'
# Internal
INTERNAL_WEBHOOK_SECRET = '<random 32-byte hex string, shared across all three workflows>'
ANOMALY_THRESHOLD_REVENUE_PCT = '15' # % deviation from prior period that triggers anomaly flag
ANOMALY_THRESHOLD_PIPELINE_PCT = '20' # % deviation from prior period that triggers anomaly flag
05Error handling and retry logic
Unhandled exceptions must never fail silently. Every integration point listed below has a defined failure behaviour. If a scenario is not covered by the table, the default behaviour is: halt the workflow, log the full error payload to the platform's error log, and post a notification to SLACK_ALERT_CHANNEL_ID with the run_id, failing step name, HTTP status code, and error message.
Integration
Scenario
Required behaviour
Xero OAuth
Access token expired (HTTP 401)
Automatically attempt token refresh using stored XERO_REFRESH_TOKEN. On refresh success, retry the original API call once. On refresh failure (e.g. refresh token revoked), halt Workflow 1, post alert to SLACK_ALERT_CHANNEL_ID, do not proceed to Agents 2 or 3. Do not retry refresh more than once without human intervention.
Xero API
Rate limit hit (HTTP 429)
Respect the Retry-After header. Pause the step for the specified number of seconds (maximum 60 seconds), then retry. Retry up to 3 times with exponential backoff (1s, 5s, 30s). After 3 failures, halt and alert.
Xero API
Unexpected empty or null response for a key field
Log the raw response. Set the affected cell value to the literal string 'DATA_MISSING' in Google Sheets. Set anomaly_flag: true in the handoff payload. Do not proceed with a zero or null value that could silently corrupt downstream KPI calculations.
HubSpot API
HTTP 401 (token invalid or revoked)
Halt Workflow 1, post alert to SLACK_ALERT_CHANNEL_ID with instruction to regenerate the Private App token and update HUBSPOT_PRIVATE_APP_TOKEN in the credential store. Do not retry automatically; token regeneration requires human action.
HubSpot API
Pagination cursor present in response (deals > 100)
Follow paging.next.link cursor in a loop until no cursor is returned. Aggregate all pages before writing to Sheets. Maximum page iterations: 20 (caps at 2,000 deals). If iteration limit is reached, log a warning and proceed with partial data, flagging anomaly_flag: true.
Google Sheets
batchUpdate returns HTTP 403 (permission denied)
Halt Workflow 1. Post alert specifying that the service account may have lost editor access to the spreadsheet. Do not retry. Human must re-share the sheet with the service account email. Log the full error response.
Google Sheets
batchUpdate returns HTTP 400 (invalid range)
Halt Workflow 1. Post alert: SHEETS_INPUT_RANGE value in credential store may be stale or the sheet tab was renamed. Do not retry. Human must update SHEETS_INPUT_RANGE in the credential store and verify the tab name.
Notion API
HTTP 404 on page creation (parent not found)
Halt Workflow 2. Post alert: NOTION_DATABASE_ID or NOTION_PARENT_PAGE_ID in credential store may be invalid or the integration lost access. Do not retry. Human must verify the Notion page sharing settings and update the credential store if required.
Notion API
HTTP 429 (rate limit)
Pause for 400ms, retry up to 3 times. If all retries fail, halt Workflow 2 and alert. Do not cascade failure to Workflow 3.
Slack API
HTTP 403 or 'not_in_channel' error on chat.postMessage
Halt Workflow 3. Post alert to SLACK_ALERT_CHANNEL_ID (if accessible) or log to platform error log. Instruction: invite the Slack app to the target channel using /invite. Do not retry automatically.
Slack API
HTTP 500 or network timeout on chat.postMessage
Retry up to 3 times with exponential backoff (2s, 10s, 30s). If all retries fail, log the full error, post a fallback alert to SLACK_ALERT_CHANNEL_ID, and record the unsent message body in the platform error log so the ops manager can post manually.
Looker Studio URL
Preflight HTTP GET returns non-200 before Slack post
Halt Agent 3's Slack post. Post alert to SLACK_ALERT_CHANNEL_ID: the dashboard share link may be broken or sharing revoked. Do not send a Slack message with a known-broken link. Human must verify and update LOOKER_STUDIO_DASHBOARD_URL in the credential store.
Internal webhook
HMAC-SHA256 signature validation fails on Agent 2 or Agent 3 trigger
Reject the payload immediately with HTTP 401. Log the rejection with the run_id and source IP. Do not execute any workflow step. Post alert to SLACK_ALERT_CHANNEL_ID. This scenario indicates a misconfiguration or unexpected external call and must be investigated before the next scheduled run.
For support with any integration error listed above, contact the FullSpec team at support@gofullspec.com. Include the run_id from the platform error log and the Slack alert message in your request to expedite diagnosis.
Integration and API SpecPage 3 of 3