FS-DOC-05Technical
Integration and API Spec
Win/Loss Analysis Automation
[YourCompany.com] · Sales Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for every service-to-service connection in the Win/Loss Analysis automation. It covers authentication methods, exact OAuth scopes, webhook configuration, field mappings between agents, credential store layout, and failure behaviour for each integration point. The FullSpec team uses this specification during build and QA; it also serves as the permanent reference if any connection needs to be reauthorised, rotated, or replaced after go-live.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Orchestration layer
Hosts all workflow logic, credential store, and inter-agent routing
Internal service account
N/A (platform-managed)
All agents
HubSpot
CRM trigger source, deal data extraction, outcome tag writeback
OAuth 2.0 (private app token)
Sales Hub Starter ($90/mo)
Agent 1, Agent 2
Typeform
Structured buyer outcome survey delivery and response ingestion
OAuth 2.0 + webhook
Basic ($29/mo)
Agent 1, Agent 2
Gmail
Buyer survey email dispatch via connected Google account
OAuth 2.0 (Google)
Google Workspace free tier
Agent 1
Slack
Rep notification with structured form, monthly report digest
OAuth 2.0 (Bot token)
Free tier sufficient
Agent 1, Agent 3
Google Sheets
Win/loss structured data tracker, analysis source for reporting
OAuth 2.0 (Google service account)
Google Workspace free tier
Agent 2, Agent 3
Notion
Monthly insight report page destination
OAuth 2.0 (internal integration token)
Plus ($16/mo)
Agent 3
Before you connect anything: all six integrations must be authorised and smoke-tested against sandbox or staging environments before any production credentials are entered into the credential store. HubSpot provides a developer sandbox; Typeform and Notion provide test workspaces; Google and Slack allow separate OAuth apps per environment. Never enter a production token until the corresponding sandbox connection has passed a live trigger test.
02Per-tool integration detail
HubSpot
Acts as the primary trigger source (deal stage change) and as a read/write data store for deal records and outcome tags. Used by Agent 1 (Deal Capture) and Agent 2 (Outcome Classification).
Auth method
OAuth 2.0 via HubSpot Private App. Generate a private app token in HubSpot Settings > Integrations > Private Apps. Store the token as HUBSPOT_PRIVATE_APP_TOKEN in the credential store. Do not use legacy API keys.
Required scopes
crm.objects.deals.read, crm.objects.deals.write, crm.objects.contacts.read, crm.objects.companies.read, crm.schemas.deals.read, timeline.events.write, oauth
Webhook/trigger setup
Configure a HubSpot CRM webhook subscription on the dealstage property. Events: property change. Filter: to value equals the internal IDs for Closed Won and Closed Lost pipeline stages. Webhook target URL is the orchestration layer inbound endpoint. Validate the X-HubSpot-Signature-Version: v3 header on every inbound request using HMAC-SHA256 with HUBSPOT_WEBHOOK_SECRET.
Required configuration
Pipeline ID (HUBSPOT_PIPELINE_ID) and stage IDs for Closed Won (HUBSPOT_STAGE_CLOSEDWON_ID) and Closed Lost (HUBSPOT_STAGE_CLOSEDLOST_ID) must be stored in the credential store, not hardcoded. Custom deal properties required: hs_wl_outcome_tag (single-line text), hs_wl_reason_code (single-line text), hs_wl_summary (multi-line text). These must be created in HubSpot before the build begins.
Rate limits
HubSpot Sales Hub Starter: 100 API calls per 10 seconds, 250,000 per day. At ~35 deals/month the automation makes roughly 6 API calls per deal (1 webhook receipt, 1 deal GET, 1 contact GET, 1 company GET, 2 deal PATCHes). Total: ~210 calls/month. No throttling required at current volume. Implement a 500 ms inter-request delay as a precaution.
Constraints
Deal stage internal IDs must be retrieved via the Pipelines API before go-live and stored as environment variables. Stage label names are not valid filter values. The private app token does not expire but must be rotated if scope changes are made.
// Inbound webhook payload (key fields)
{ "subscriptionType": "deal.propertyChange",
"objectId": 12345678,
"propertyName": "dealstage",
"propertyValue": "<stage_id_closedwon|closedlost>" }
// Deal GET response (fields extracted)
dealname, amount, closedate, hubspot_owner_id,
hs_num_associated_contacts, pipeline, dealstage
// PATCH body for outcome writeback
{ "properties": {
"hs_wl_outcome_tag": "Won - Champion",
"hs_wl_reason_code": "W-CHAMP",
"hs_wl_summary": "<150-char AI summary>" } }Typeform
Hosts the 8-question structured buyer outcome survey. Agent 1 dispatches a pre-filled survey link via Gmail. Agent 2 ingests the completed response via webhook.
Auth method
OAuth 2.0. Authorise via Typeform's OAuth flow and store the access token as TYPEFORM_ACCESS_TOKEN. Refresh token stored as TYPEFORM_REFRESH_TOKEN. Token TTL is not fixed; implement refresh-on-401 logic.
Required scopes
forms:read, responses:read, webhooks:write, webhooks:read
Webhook/trigger setup
Register a response webhook on the buyer survey form (TYPEFORM_FORM_ID) via the Typeform Webhooks API (POST /forms/{form_id}/webhooks). Payload: all fields. Validate inbound requests using the Typeform-Signature header (SHA256 HMAC, secret stored as TYPEFORM_WEBHOOK_SECRET). The webhook fires on every completed submission.
Required configuration
TYPEFORM_FORM_ID stored in the credential store. Hidden fields pre-filled via URL parameters: deal_id, deal_name, company_name, rep_name. The survey form must include a hidden field block for each of these four values so they are returned in the response payload and can be used to match the response to the originating deal.
Rate limits
Typeform Basic: 4,000 responses/month, 200 API requests/minute. At 35 surveys/month and ~5 API calls per deal cycle, usage is well within limits. No throttling needed.
Constraints
Pre-filled hidden fields must be URL-encoded. Typeform does not support conditional webhook filtering; all submissions from the form will fire the webhook regardless of which deal triggered the send. The agent must match responses by the deal_id hidden field value.
// Webhook response payload (key fields)
{ "form_id": "<TYPEFORM_FORM_ID>",
"token": "<response_token>",
"hidden": { "deal_id": "12345678", "deal_name": "Acme Q2",
"company_name": "Acme Corp", "rep_name": "Sam Okafor" },
"answers": [
{ "field": { "ref": "primary_outcome_reason" }, "choice": { "label": "Pricing" } },
{ "field": { "ref": "competitor_named" }, "text": "Competitor X" },
{ "field": { "ref": "nps_score" }, "number": 7 }
] }Gmail
Sends the personalised Typeform survey link to the primary buyer contact. Used exclusively by Agent 1. Sending is done via the Gmail API on behalf of the authenticated Google account.
Auth method
OAuth 2.0 via Google. Authorise the sending account (a dedicated sales automation Gmail address, not a personal rep account). Store credentials as GMAIL_OAUTH_CLIENT_ID, GMAIL_OAUTH_CLIENT_SECRET, GMAIL_OAUTH_REFRESH_TOKEN, and GMAIL_SENDER_ADDRESS in the credential store.
Required scopes
https://www.googleapis.com/auth/gmail.send
Webhook/trigger setup
Gmail is write-only in this automation; no inbound webhook is required. The automation sends a single email per deal close event. No Gmail push notifications are needed.
Required configuration
GMAIL_SURVEY_TEMPLATE_ID: the ID of the saved email template in the credential store (not hardcoded in the workflow). Template uses placeholders: {{buyer_first_name}}, {{deal_name}}, {{company_name}}, {{typeform_prefilled_url}}. GMAIL_FROM_NAME stored separately for the display name header.
Rate limits
Google Workspace (free tier): 500 messages/day per user via the API. At 35 deals/month (~1.2/day) this is well within limits. No throttling required.
Constraints
The sending address must have Gmail API enabled in the Google Cloud project. OAuth consent screen must be published (not in testing mode) or the refresh token will expire after 7 days. Use a dedicated service account email rather than a personal rep address to avoid permission conflicts.
// Gmail API send body (Messages.send)
{ "raw": "<base64url-encoded RFC 2822 message>" }
// Constructed email headers
From: Win/Loss Insights <automation@[YourCompany.com]>
To: {{buyer_email}}
Subject: Quick question about your decision — {{deal_name}}
Content-Type: text/html
// Response (success)
{ "id": "<message_id>", "threadId": "<thread_id>", "labelIds": ["SENT"] }Integration and API SpecPage 1 of 4
FS-DOC-05Technical
Slack
Delivers the rep notification with embedded structured form (Agent 1) and posts the monthly report digest to the sales channel (Agent 3). Uses a Slack Bot token with Block Kit payloads.
Auth method
OAuth 2.0 Bot token. Install the Slack app to the workspace and store the bot token as SLACK_BOT_TOKEN. Store the signing secret as SLACK_SIGNING_SECRET for inbound payload validation.
Required scopes
chat:write, chat:write.public, commands, im:write, users:read, users:read.email, channels:read, blocks (implicit via chat.postMessage)
Webhook/trigger setup
Enable Interactivity in the Slack app settings and set the Request URL to the orchestration layer endpoint for Block Kit action payloads. Validate all inbound payloads using the X-Slack-Signature header (HMAC-SHA256, v0 scheme) with SLACK_SIGNING_SECRET. The rep notification uses a Block Kit modal-style message with a static_select for outcome reason, a plain_text_input for competitor name, and a plain_text_input for rep notes. Action IDs: wl_outcome_reason, wl_competitor, wl_rep_notes, wl_submit.
Required configuration
SLACK_REP_DM_CHANNEL: resolved at runtime from the rep's Slack user ID (looked up via users.lookupByEmail using the HubSpot deal owner email). SLACK_SALES_CHANNEL_ID: the channel ID for the monthly digest (stored in credential store, not the channel name). SLACK_BOT_USER_ID stored for self-mention suppression.
Rate limits
Slack free tier: 1 message per second per channel (Tier 1). At 35 deal notifications/month plus 1 digest/month, peak volume is well within limits. No throttling required.
Constraints
Block Kit interactive messages expire after 30 minutes for button actions but the submission payload is still accepted. The 24-hour response window communicated to the rep is a business rule, not a platform limit. Slack does not support pre-filling form fields from a message; all taxonomy options must be presented as a list in the static_select element.
// chat.postMessage payload (rep notification)
{ "channel": "<rep_slack_user_id>",
"blocks": [
{ "type": "section", "text": { "type": "mrkdwn",
"text": "*Deal closed: {{deal_name}}* ({{outcome}}). Please log your outcome below." } },
{ "type": "actions", "elements": [
{ "type": "static_select", "action_id": "wl_outcome_reason",
"placeholder": { "type": "plain_text", "text": "Select primary reason" },
"options": [ <taxonomy options from TAXONOMY_JSON> ] }
] }
] }
// Inbound Block Kit interaction payload (key fields)
{ "type": "block_actions",
"user": { "id": "U0123ABC", "email": "sam@[YourCompany.com]" },
"actions": [ { "action_id": "wl_outcome_reason",
"selected_option": { "value": "W-CHAMP" } } ] }Google Sheets
Serves as the persistent win/loss data tracker. Agent 2 appends a structured row per closed deal. Agent 3 reads all rows from the prior calendar month to generate the insight report.
Auth method
OAuth 2.0 via Google service account. Create a service account in Google Cloud, download the JSON key, and store the key file contents as GSHEETS_SERVICE_ACCOUNT_JSON. Share the target spreadsheet with the service account email address (editor permission).
Required scopes
https://www.googleapis.com/auth/spreadsheets
Webhook/trigger setup
Google Sheets does not support native outbound webhooks. Agent 3 polls the sheet on a scheduled trigger (first business day of each month, 07:00 local time). No inbound webhook configuration needed.
Required configuration
GSHEETS_SPREADSHEET_ID and GSHEETS_SHEET_NAME (tab name, default: WinLoss_Tracker) stored in the credential store. Column order must match the append payload exactly. Required columns (A through M): deal_id, deal_name, company_name, rep_name, close_date, outcome, outcome_tag, reason_code, deal_value, competitor, buyer_response_summary, rep_notes, row_created_at. A header row must exist before the first append.
Rate limits
Google Sheets API v4: 300 read requests/minute, 300 write requests/minute per project. At 35 rows/month appended and 1 monthly bulk read, usage is negligible. No throttling required.
Constraints
All date values must be written as ISO 8601 strings (YYYY-MM-DD) to ensure correct sorting. Do not rely on Google Sheets auto-formatting for date columns. The service account must not be given owner permissions. If the spreadsheet is moved to a different Drive folder, the SPREADSHEET_ID remains valid and no credential change is needed.
// Sheets API append body (values.append)
{ "range": "WinLoss_Tracker!A:M",
"majorDimension": "ROWS",
"values": [[
"12345678", // deal_id
"Acme Q2", // deal_name
"Acme Corp", // company_name
"Sam Okafor", // rep_name
"2024-06-01", // close_date
"Lost", // outcome
"Pricing", // outcome_tag
"L-PRICE", // reason_code
"18500", // deal_value
"Competitor X", // competitor
"Chose on price", // buyer_response_summary
"Rep notes text", // rep_notes
"2024-06-01T09:14:00Z" // row_created_at
]] }Notion
Receives the monthly insight report as a new structured page in a designated database. Used exclusively by Agent 3 on the first business day of each month.
Auth method
OAuth 2.0 via Notion internal integration. Create an internal integration in Notion Settings > Integrations. Store the integration secret as NOTION_INTEGRATION_TOKEN. Share the target database with the integration (Share button on the database page).
Required scopes
Notion internal integrations use capability toggles rather than OAuth scope strings. Required capabilities: Read content, Update content, Insert content. No user information capability needed.
Webhook/trigger setup
Notion does not support outbound webhooks. Agent 3 writes to Notion on a scheduled trigger only. No inbound webhook configuration needed.
Required configuration
NOTION_DATABASE_ID: the ID of the Win/Loss Reports database (32-character string from the database URL, stored in credential store). Required database properties: Report Month (date), Outcome Summary (rich text), Top Win Drivers (rich text), Top Loss Reasons (rich text), Competitor Mentions (rich text), Created By (select, default: Automation), Status (select, default: Published).
Rate limits
Notion API: 3 requests/second average. Page creation involves 1 database page create call plus up to 5 block append calls for content sections. At 1 report/month, rate limiting is not relevant. Implement a 400 ms delay between block append calls as a precaution.
Constraints
Notion block content must be split into chunks of no more than 100 blocks per append call. Report pages should not exceed 50 blocks in practice. The NOTION_DATABASE_ID must be re-confirmed if the database is duplicated or moved, as the ID changes. Rich text fields have a 2,000-character limit per cell; summaries must be truncated before writing.
// Notion pages.create body
{ "parent": { "database_id": "<NOTION_DATABASE_ID>" },
"properties": {
"Report Month": { "date": { "start": "2024-06-01" } },
"Status": { "select": { "name": "Published" } },
"Created By": { "select": { "name": "Automation" } }
},
"children": [
{ "object": "block", "type": "heading_2",
"heading_2": { "rich_text": [{ "text": { "content": "Win/Loss Summary: June 2024" } }] } },
{ "object": "block", "type": "paragraph",
"paragraph": { "rich_text": [{ "text": { "content": "<AI-generated summary>" } }] } }
] }Integration and API SpecPage 2 of 4
FS-DOC-05Technical
03Field mappings between tools
The following tables map every field passed between agents at each handoff point. Field names are shown exactly as they appear in the source and destination system payloads.
Agent 1 handoff: HubSpot trigger to Gmail and Slack dispatch
Source tool
Source field
Destination tool
Destination field
HubSpot
`properties.dealname`
Gmail
`{{deal_name}}`
HubSpot
`properties.amount`
Google Sheets
`deal_value`
HubSpot
`properties.closedate`
Google Sheets
`close_date`
HubSpot
`properties.hubspot_owner_id`
Slack
`channel` (resolved via users.lookupByEmail)
HubSpot
`associations.contacts[0].id`
Gmail
`To:` header (resolved via Contacts GET)
HubSpot
`associations.contacts[0].properties.firstname`
Gmail
`{{buyer_first_name}}`
HubSpot
`associations.companies[0].properties.name`
Gmail + Typeform
`{{company_name}}` / hidden field `company_name`
HubSpot
`objectId`
Typeform
hidden field `deal_id`
HubSpot
`properties.dealstage`
Internal state
`outcome` (mapped: closedwon=Won, closedlost=Lost)
Agent 2 handoff: Typeform response and Slack form submission to HubSpot and Google Sheets
Source tool
Source field
Destination tool
Destination field
Typeform
`hidden.deal_id`
HubSpot
Deal object ID (PATCH target)
Typeform
`answers[primary_outcome_reason].choice.label`
AI classifier
`buyer_stated_reason` (input)
Typeform
`answers[competitor_named].text`
Google Sheets
`competitor`
Typeform
`answers[nps_score].number`
Google Sheets
`buyer_nps`
Slack
`actions[wl_outcome_reason].selected_option.value`
AI classifier
`rep_stated_reason_code` (input)
Slack
`actions[wl_competitor].value`
Google Sheets
`competitor`
Slack
`actions[wl_rep_notes].value`
Google Sheets
`rep_notes`
AI classifier output
`outcome_tag`
HubSpot
`properties.hs_wl_outcome_tag`
AI classifier output
`reason_code`
HubSpot
`properties.hs_wl_reason_code`
AI classifier output
`summary`
HubSpot
`properties.hs_wl_summary`
AI classifier output
`outcome_tag`
Google Sheets
`outcome_tag`
AI classifier output
`reason_code`
Google Sheets
`reason_code`
Agent 3 handoff: Google Sheets rows to Notion page and Slack digest
Source tool
Source field
Destination tool
Destination field
Google Sheets
`outcome` (filtered: prior month rows)
AI report generator
`outcome_list` (input array)
Google Sheets
`reason_code`
AI report generator
`reason_code_list` (input array)
Google Sheets
`competitor`
AI report generator
`competitor_list` (input array)
Google Sheets
`deal_value`
AI report generator
`deal_value_list` (input for weighting)
AI report output
`top_win_drivers` (array of 3)
Notion
`properties.Top Win Drivers` (rich text)
AI report output
`top_loss_reasons` (array of 3)
Notion
`properties.Top Loss Reasons` (rich text)
AI report output
`competitor_mentions`
Notion
`properties.Competitor Mentions` (rich text)
AI report output
`executive_summary`
Notion
Page body heading_2 + paragraph blocks
AI report output
`slack_digest_text`
Slack
`blocks[section].text.text` (mrkdwn)
Notion
`url` (page URL from create response)
Slack
`blocks[section].text.text` (appended as link)
04Build stack and orchestration
Orchestration layout
Three discrete workflows, one per agent. Each workflow is independently deployable and testable. Shared credential store is mounted at the environment level and accessed by all three workflows. No workflow calls another workflow directly; inter-agent data is passed via the internal state object and Google Sheets as the durable store.
Agent 1 trigger
Webhook (push). HubSpot fires a CRM webhook to the orchestration layer inbound URL on every dealstage property change. The workflow validates the X-HubSpot-Signature-Version v3 HMAC signature before processing. Latency target: begin execution within 30 seconds of deal stage change.
Agent 2 trigger
Webhook (push), dual-source. Two inbound webhook endpoints: one for Typeform response submissions (validated via Typeform-Signature HMAC), one for Slack Block Kit interaction payloads (validated via X-Slack-Signature HMAC). The workflow waits for either payload keyed on deal_id, with a 7-day timeout window before escalation.
Agent 3 trigger
Schedule (poll). Fires at 07:00 on the first business day of each calendar month. Business day determination uses a simple weekday check (Monday through Friday); public holiday handling is not implemented in v1. The workflow reads Google Sheets, runs the AI summarisation step, writes to Notion, and posts to Slack sequentially.
Credential store
All secrets are stored as encrypted environment variables in the orchestration platform's native secret manager. No credentials appear in workflow node configuration fields or in code. Rotation of any token requires updating the secret store entry only; no workflow edits are needed.
Logging
Every workflow execution writes a structured log entry on start, on each tool call (success and failure), and on completion. Log retention: 90 days. Errors are also written to a dedicated SLACK_ALERT_CHANNEL_ID for immediate visibility.
All secrets must be stored here. No credential value may appear in a workflow node, environment file committed to version control, or log output.
// Credential store contents (all values encrypted at rest)
// HubSpot
HUBSPOT_PRIVATE_APP_TOKEN=<private_app_token>
HUBSPOT_WEBHOOK_SECRET=<hmac_secret>
HUBSPOT_PIPELINE_ID=<pipeline_id>
HUBSPOT_STAGE_CLOSEDWON_ID=<stage_id>
HUBSPOT_STAGE_CLOSEDLOST_ID=<stage_id>
// Typeform
TYPEFORM_ACCESS_TOKEN=<oauth_access_token>
TYPEFORM_REFRESH_TOKEN=<oauth_refresh_token>
TYPEFORM_FORM_ID=<form_id>
TYPEFORM_WEBHOOK_SECRET=<hmac_secret>
// Gmail (Google OAuth)
GMAIL_OAUTH_CLIENT_ID=<google_client_id>
GMAIL_OAUTH_CLIENT_SECRET=<google_client_secret>
GMAIL_OAUTH_REFRESH_TOKEN=<google_refresh_token>
GMAIL_SENDER_ADDRESS=automation@[YourCompany.com]
GMAIL_FROM_NAME=Win/Loss Insights
GMAIL_SURVEY_TEMPLATE_ID=<template_id_in_store>
// Slack
SLACK_BOT_TOKEN=xoxb-<token>
SLACK_SIGNING_SECRET=<signing_secret>
SLACK_SALES_CHANNEL_ID=C0123ABCDEF
SLACK_ALERT_CHANNEL_ID=C0456GHIJKL
SLACK_BOT_USER_ID=U0789MNOPQR
// Google Sheets
GSHEETS_SERVICE_ACCOUNT_JSON=<json_key_contents>
GSHEETS_SPREADSHEET_ID=<spreadsheet_id>
GSHEETS_SHEET_NAME=WinLoss_Tracker
// Notion
NOTION_INTEGRATION_TOKEN=secret_<token>
NOTION_DATABASE_ID=<32-char-database-id>
// Taxonomy
TAXONOMY_JSON=<json_string_of_win_loss_reason_categories>
// AI classification
AI_CLASSIFIER_API_KEY=<api_key>
AI_CLASSIFIER_MODEL=<model_id>
AI_CLASSIFIER_ENDPOINT=<endpoint_url>
Integration and API SpecPage 3 of 4
FS-DOC-05Technical
05Error handling and retry logic
Unhandled exceptions must never fail silently. Every integration failure must produce a structured log entry and, where the failure affects deal data capture, an alert to SLACK_ALERT_CHANNEL_ID within 60 seconds of the error. Silent drops of deal events are the highest-risk failure mode for this automation.
Integration
Scenario
Required behaviour
HubSpot webhook
Webhook signature validation fails
Return HTTP 400 immediately. Log the raw payload hash. Do not process. Alert SLACK_ALERT_CHANNEL_ID if 3 or more failures occur within 10 minutes (possible secret rotation mismatch).
HubSpot API
Deal GET returns 404 (deal deleted)
Abort workflow for this execution. Log deal_id and HTTP status. No retry. Post alert to SLACK_ALERT_CHANNEL_ID with deal_id for manual review.
HubSpot API
Deal PATCH (outcome writeback) returns 429 or 5xx
Retry with exponential backoff: 30 s, 2 min, 5 min (3 attempts). If all retries fail, write outcome data to Google Sheets only and flag the Sheets row with hubspot_sync_failed=TRUE. Alert SLACK_ALERT_CHANNEL_ID.
Gmail
Survey email send fails (5xx or OAuth error)
Retry twice with 60 s delay. If both retries fail, log the failure and alert SLACK_ALERT_CHANNEL_ID with deal_id and buyer email. Manual send required. Do not block Agent 1 rep notification step.
Gmail
OAuth token expired (401)
Attempt token refresh using GMAIL_OAUTH_REFRESH_TOKEN before retry. If refresh fails, alert SLACK_ALERT_CHANNEL_ID immediately. Token reauthorisation required by FullSpec.
Typeform webhook
No buyer survey response received within 7 days
Agent 2 timeout fires. Set buyer_response_summary=NO_RESPONSE in Sheets row. If deal_value exceeds $10,000 and outcome=Lost, post escalation message to SLACK_SALES_CHANNEL_ID tagging the sales manager for optional manual follow-up.
Typeform webhook
Duplicate response received for same deal_id
Deduplicate by storing processed response tokens in the execution state. If token already logged, discard payload and return HTTP 200 to Typeform. Log the deduplication event.
Slack
Rep notification DM fails (user not found or channel error)
Retry once after 2 minutes. If retry fails, fall back to posting the notification to SLACK_SALES_CHANNEL_ID with the rep's display name. Log failure and alert SLACK_ALERT_CHANNEL_ID.
Slack
Rep does not submit Block Kit form within 24 hours
Agent 2 partial timeout. Proceed with Typeform response only if available. If neither source has responded, apply NO_RESPONSE logic as above. Do not block the Google Sheets row creation.
Google Sheets
Append call fails (5xx or quota exceeded)
Retry with exponential backoff: 1 min, 3 min, 10 min (3 attempts). If all retries fail, write the structured row to a RECOVERY_QUEUE log in the credential store for manual re-insertion. Alert SLACK_ALERT_CHANNEL_ID.
Notion
Page create or block append fails (5xx or rate limit)
Retry up to 3 times with 5 s delay between attempts. If all retries fail, post the full report text as a Slack message to SLACK_SALES_CHANNEL_ID with a note that the Notion write failed. Log failure and alert SLACK_ALERT_CHANNEL_ID. Do not suppress the monthly report.
AI classifier
Classification API returns error or timeout
Retry once after 30 s. If retry fails, write outcome_tag=UNCLASSIFIED and reason_code=MANUAL_REVIEW to HubSpot and Google Sheets. Flag the Sheets row with classification_status=FAILED. Alert SLACK_ALERT_CHANNEL_ID so the manager can manually assign the taxonomy category.
For all retry sequences, the maximum total retry window must not exceed 15 minutes for deal-capture events (Agents 1 and 2) and 30 minutes for reporting events (Agent 3). Any failure that exhausts all retries must result in a logged error record, a Slack alert, and a clearly flagged row or entry so the FullSpec team or the process owner can identify and resolve the gap without data loss.
For questions about this specification or to report a credential or integration issue, contact the FullSpec team at support@gofullspec.com.
Integration and API SpecPage 4 of 4