FS-DOC-05Technical
Integration and API Spec
Upsell and Cross-sell Triggers
[YourCompany.com] · Sales Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative integration reference for the Upsell and Cross-sell Triggers automation. It covers every tool connected to the workflow, the exact authentication scopes and configuration required for each, field-level mappings between systems, orchestration layout, credential store contents, and defined error handling behaviour for every integration point. The FullSpec team uses this document to build and validate all connections. No credentials should ever be hardcoded; all sensitive values are stored in the credential store as documented in Section 04.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
Segment
Qualifying event source and workflow trigger
HTTP webhook (inbound) + Segment source write key
Team ($120/mo)
Trigger layer (pre-agent)
HubSpot
CRM data fetch and activity logging
OAuth 2.0
Sales Hub Starter ($90/mo)
Agent 1 (Offer Selection), Agent 2 (Outreach Drafting)
Gmail
Outreach email delivery and rep review queue
OAuth 2.0 (Google Workspace)
Google Workspace Business Starter (included)
Agent 2 (Outreach Drafting)
Slack
Rep notification on send
OAuth 2.0 (Bot token)
Free tier or above (included)
Agent 2 (Outreach Drafting)
Google Sheets
Outcome and performance tracking; offer catalogue source
OAuth 2.0 (Google Workspace)
Google Workspace Business Starter (included)
Agent 1 (Offer Selection), post-send logging step
Workflow automation tool
Orchestration layer: hosts all workflows, manages credentials, routes data between tools and agents
Platform-native credential store
Appropriate tier for webhook + API step volume
All agents
Before you connect anything: every integration must be tested against sandbox or development credentials before any production credentials are entered. For HubSpot, use a developer sandbox account. For Segment, use a dedicated development source. For Gmail and Google Sheets, use a non-production Google Workspace user. Segment and Slack do not offer sandbox environments natively; use test workspaces or test channels isolated from live data. Only after all connections are verified in sandbox should production credentials be entered into the credential store.
02Per-tool integration detail
Segment
Segment acts as the event source that initiates the entire workflow. A qualifying customer event (spend milestone, usage threshold, or renewal within 30 days) fires from Segment to the orchestration layer via a webhook destination. The orchestration layer validates the incoming payload signature before processing.
Auth method
Inbound webhook with HMAC-SHA256 signature validation using the Segment webhook shared secret. The secret is stored in the credential store as SEGMENT_WEBHOOK_SECRET.
Required scopes
No outbound OAuth scopes required for inbound webhook receipt. If using the Segment Public API to query source schemas: workspace:read, sources:read.
Webhook / trigger setup
In the Segment workspace, navigate to Connections > Destinations > Webhook. Add the orchestration layer's inbound webhook URL as the destination endpoint. Enable signature validation and copy the shared secret to the credential store. Subscribe to the following event names: upsell_qualifying_event, usage_threshold_reached, renewal_approaching. Map the userId and traits properties to the downstream payload schema defined in Section 03.
Required configuration
Segment source write key stored as SEGMENT_WRITE_KEY in credential store (for any reverse-lookup calls). Event names must match exactly: upsell_qualifying_event, usage_threshold_reached, renewal_approaching. Property names must be consistent across all Segment sources (web, billing, product). Confirm taxonomy with the sales and product teams before go-live.
Rate limits
Segment Webhook Destination: no documented hard rate limit on inbound webhook delivery. Volume at ~120 events/month (approximately 4 events/day) is well below any practical throttling threshold. No throttling logic required at current volume.
Constraints
Segment does not guarantee exactly-once delivery; the orchestration layer must implement idempotency using the Segment messageId field to prevent duplicate processing. Webhook retries from Segment use exponential backoff with up to 4 attempts over 72 hours if the endpoint returns a non-2xx response.
// Inbound webhook payload (key fields)
messageId : string // Segment-generated unique event ID; use for idempotency check
event : string // e.g. 'upsell_qualifying_event'
userId : string // Segment userId, maps to HubSpot contact lookup
properties.trigger_type : string // 'spend' | 'usage' | 'renewal'
properties.spend_total_usd : number // cumulative spend to date
properties.usage_pct : number // usage as % of plan limit
properties.renewal_days : number // days until renewal
properties.account_tier : string // 'standard' | 'high_value'
timestamp : ISO 8601 string
// Output to orchestration layer
Validated payload forwarded to HubSpot fetch step; messageId stored for dedup check
HubSpot
HubSpot is used in two distinct phases: first to fetch the full CRM contact and company record for the triggered customer (consumed by Agent 1), and second to log the outreach activity and offer details after the email is sent (executed by Agent 2). Both operations use the same OAuth 2.0 connection.
Auth method
OAuth 2.0. Access token stored as HUBSPOT_ACCESS_TOKEN; refresh token stored as HUBSPOT_REFRESH_TOKEN. Token refresh is handled automatically by the orchestration layer using the HubSpot token endpoint: https://api.hubapi.com/oauth/v1/token.
Required scopes
crm.objects.contacts.read, crm.objects.contacts.write, crm.objects.companies.read, crm.objects.deals.read, crm.schemas.contacts.read, timeline.events.write, sales-email-read
Webhook / trigger setup
HubSpot is not a trigger source in this automation. All reads and writes are initiated by the orchestration layer. No HubSpot webhook subscription is required.
Required configuration
HubSpot portal ID stored as HUBSPOT_PORTAL_ID. Custom timeline event type ID stored as HUBSPOT_TIMELINE_EVENT_TYPE_ID (must be created in HubSpot developer settings before build). The activity log entry uses this event type to record: trigger_event, offer_name, offer_id, send_timestamp, send_status. Pipeline stage IDs for 'Upsell Initiated' must be retrieved from the HubSpot API and stored as HUBSPOT_PIPELINE_STAGE_ID. All IDs stored in credential store, not hardcoded.
Rate limits
HubSpot API: 100 requests/10 seconds per OAuth token (standard tier). At ~120 events/month, peak load is 2 API calls per event (1 read, 1 write) = approximately 240 calls/month. No throttling required. Implement retry with backoff on 429 responses regardless.
Constraints
HubSpot contact lookup must use the Segment userId mapped to the HubSpot contact property hs_external_id or email. Confirm the mapping field with the Revenue Ops team before build. Write operations must be idempotent: check for an existing timeline event with the same messageId before creating a duplicate entry.
// Input to fetch step (from Segment payload)
userId or email : string // used to query contacts API
// GET /crm/v3/objects/contacts/{id}?properties=email,firstname,lastname,account_tier,
// hs_external_id,hubspot_owner_id,hs_lead_status,lifecyclestage
// GET /crm/v3/objects/companies/{id}?properties=name,industry,annualrevenue,hs_num_associated_deals
// Output fields passed to Agent 1
contact.email, contact.firstname, contact.lastname, contact.account_tier,
contact.hubspot_owner_id, company.name, company.industry,
open_deals (count), last_activity_date
// Input to activity log step (from Agent 2)
contact_id, offer_name, offer_id, trigger_event, send_timestamp, send_status
// POST /crm/v3/timeline/events (custom timeline event)
// PATCH /crm/v3/objects/contacts/{id} (update hs_lead_status = 'Upsell Initiated')Gmail
Gmail is used by Agent 2 to either send the outreach email automatically (standard accounts) or to create a draft in the assigned sales rep's mailbox for manual review and approval (high-value accounts). Both paths use the same OAuth 2.0 connection scoped to the sending mailbox.
Auth method
OAuth 2.0 using a Google Workspace service account with domain-wide delegation, OR per-user OAuth 2.0 with refresh token. Access token stored as GMAIL_ACCESS_TOKEN; refresh token stored as GMAIL_REFRESH_TOKEN. The sending address is stored as GMAIL_SENDER_ADDRESS.
Required scopes
https://www.googleapis.com/auth/gmail.send, https://www.googleapis.com/auth/gmail.compose, https://www.googleapis.com/auth/gmail.modify (required to label sent items and create drafts)
Webhook / trigger setup
Gmail is not a trigger source in this automation. The orchestration layer calls the Gmail API on demand. No Gmail push notification subscription is required for the send or draft creation path.
Required configuration
GMAIL_SENDER_ADDRESS: the outbound email address used for all automated sends. GMAIL_DRAFT_LABEL: Gmail label name applied to drafts queued for rep review (e.g. 'FullSpec-Upsell-Review'). GMAIL_HIGH_VALUE_LABEL: label applied to sent emails for high-value accounts post-approval. Email template placeholder mapping: {{first_name}}, {{company_name}}, {{offer_name}}, {{offer_description}}, {{account_context}}, {{rep_name}}, {{rep_signature}}. Template HTML stored in credential store or a versioned configuration sheet, not hardcoded in the workflow.
Rate limits
Gmail API: 250 quota units/second per user; send message = 100 units, create draft = 100 units. At ~120 events/month, volume is negligible. No throttling required. Implement retry on 429 and 500 responses.
Constraints
Automated commercial email must comply with CAN-SPAM and GDPR as applicable. Unsubscribe headers must be included in all automated sends. The from address must be a verified Google Workspace address. Draft creation for high-value accounts must include a subject line prefix of '[REVIEW REQUIRED]' so reps can filter their queue reliably.
// Input to send step (from Agent 2)
to_email : string // customer email from HubSpot contact record
subject : string // generated by Agent 2 using offer_name + account context
body_html : string // personalised HTML body with placeholders resolved
from_address : string // value of GMAIL_SENDER_ADDRESS
reply_to : string // assigned rep email from hubspot_owner_id lookup
account_tier : string // 'standard' -> send | 'high_value' -> create draft
// POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send (standard accounts)
// POST https://gmail.googleapis.com/gmail/v1/users/me/drafts (high-value accounts)
// Output
message_id : string // Gmail message ID, stored in HubSpot activity log
thread_id : string // Gmail thread ID, stored for response monitoring
send_status : 'sent' | 'draft_created' | 'failed'
Integration and API SpecPage 1 of 3
FS-DOC-05Technical
Slack
Slack is used by Agent 2 to notify the assigned account owner immediately after an outreach email is sent or a draft is queued for review. The message includes the offer name, the customer name, and a direct link to the HubSpot contact record. A bot token is used for all Slack API calls.
Auth method
OAuth 2.0 Bot Token. Token stored as SLACK_BOT_TOKEN. The Slack app must be installed to the workspace with the bot token scopes listed below.
Required scopes
chat:write, users:read, users:read.email (to resolve HubSpot owner email to Slack user ID)
Webhook / trigger setup
Slack is not a trigger source. All messages are posted by the orchestration layer using the Slack Web API chat.postMessage method. No incoming webhook or event subscription is required.
Required configuration
SLACK_BOT_TOKEN: bot OAuth token from the Slack app configuration page. SLACK_FALLBACK_CHANNEL_ID: the Slack channel ID used when the account owner's Slack user ID cannot be resolved from their email (e.g. #sales-alerts). SLACK_APP_ID: stored for audit reference. Owner-to-Slack-user mapping is resolved at runtime using users:read.email against the rep's email from HubSpot hubspot_owner_id. Channel and user IDs must never be hardcoded; resolve dynamically or store in a configuration sheet.
Rate limits
Slack Web API: chat.postMessage is Tier 3 (50+ requests/minute burst). At ~120 messages/month, volume is negligible. No throttling required. Implement retry with backoff on rate_limited responses.
Constraints
If the Slack user ID for the account owner cannot be resolved, fall back to posting to SLACK_FALLBACK_CHANNEL_ID with the owner's name included in the message text. Never silently drop a notification. Message text must include: customer first name, company name, offer name, send status (sent or awaiting review), and a HubSpot deep link in the format https://app.hubspot.com/contacts/{HUBSPOT_PORTAL_ID}/contact/{contact_id}.
// Input to Slack notification step
owner_email : string // from HubSpot hubspot_owner_id -> owner email lookup
customer_name : string // contact.firstname + contact.lastname
company_name : string // company.name
offer_name : string // from Agent 1 output
send_status : string // 'Sent automatically' | 'Awaiting your review'
hubspot_link : string // deep link to HubSpot contact record
// Runtime resolution
slack_user_id : resolved via users.lookupByEmail(owner_email)
// POST https://slack.com/api/chat.postMessage
// Output
ts : string // Slack message timestamp, stored for audit log
Google Sheets
Google Sheets serves two roles: as a read source for Agent 1 (the product offer catalogue and scoring rules) and as a write destination for the final outcome logging step (customer, offer, send date, account owner, send status). Both operations use the same OAuth 2.0 connection.
Auth method
OAuth 2.0 using a Google Workspace service account with the Sheets API enabled, OR per-user OAuth 2.0 with refresh token. Access token stored as GSHEETS_ACCESS_TOKEN; refresh token stored as GSHEETS_REFRESH_TOKEN.
Required scopes
https://www.googleapis.com/auth/spreadsheets (read and write to specific spreadsheets)
Webhook / trigger setup
Google Sheets is not a trigger source. All reads and writes are initiated by the orchestration layer on demand.
Required configuration
GSHEETS_SPREADSHEET_ID: the ID of the master tracking and catalogue spreadsheet, stored in the credential store. GSHEETS_CATALOGUE_RANGE: the named range or A1 notation for the offer catalogue sheet (e.g. 'OfferCatalogue!A2:G'). GSHEETS_TRACKING_RANGE: the named range for the outcome tracking sheet (e.g. 'OutcomeLog!A:H'). Expected catalogue sheet columns: offer_id, offer_name, offer_description, eligible_tiers, min_spend_usd, min_usage_pct, priority_score. Expected tracking sheet columns: timestamp, customer_email, company_name, trigger_type, offer_name, send_status, account_owner, hubspot_link. Column order must match exactly; any structural change to the sheet requires a corresponding workflow update.
Rate limits
Google Sheets API: 300 read requests/minute per project, 60 write requests/minute per user. At ~120 append operations/month, volume is negligible. No throttling required. Implement retry with exponential backoff on 429 responses.
Constraints
The offer catalogue sheet is the single source of truth for scoring rules. It must be version-controlled (a changelog tab is recommended). The orchestration layer reads the catalogue at runtime on each event; it must not cache catalogue data for more than 1 hour to ensure rule changes take effect promptly. The tracking sheet must be append-only from the automation; no rows should be deleted or modified by the workflow after initial write.
// Input: Agent 1 catalogue read
// GET https://sheets.googleapis.com/v4/spreadsheets/{GSHEETS_SPREADSHEET_ID}
// /values/{GSHEETS_CATALOGUE_RANGE}
// Returns rows: offer_id, offer_name, offer_description, eligible_tiers,
// min_spend_usd, min_usage_pct, priority_score
// Filtered against: contact.account_tier, spend_total_usd, usage_pct
// Input: outcome logging step (post-send)
row_data: [timestamp, customer_email, company_name, trigger_type,
offer_name, send_status, account_owner_email, hubspot_link]
// POST https://sheets.googleapis.com/v4/spreadsheets/{GSHEETS_SPREADSHEET_ID}
// /values/{GSHEETS_TRACKING_RANGE}:append?valueInputOption=USER_ENTERED
// Output
updatedRange : string // confirmed appended range (e.g. 'OutcomeLog!A121:H121')03Field mappings between tools
The following tables define the exact field-level mappings for each agent handoff in the workflow. All field names are in monospace notation as they appear in the respective tool APIs. Any field not listed here is not consumed or written by the automation.
Handoff 1: Segment trigger to HubSpot fetch (pre-Agent 1)
Source tool
Source field
Destination tool
Destination field
Segment
userId
HubSpot API
filter: hs_external_id
Segment
properties.trigger_type
Orchestration context
trigger_type
Segment
properties.spend_total_usd
Orchestration context
spend_total_usd
Segment
properties.usage_pct
Orchestration context
usage_pct
Segment
properties.renewal_days
Orchestration context
renewal_days
Segment
properties.account_tier
Orchestration context
account_tier
Segment
messageId
Orchestration context
dedup_key
Segment
timestamp
Orchestration context
event_timestamp
Handoff 2: HubSpot fetch to Agent 1 (Offer Selection Agent)
Source tool
Source field
Destination tool
Destination field
HubSpot
contact.email
Agent 1 input
customer_email
HubSpot
contact.firstname
Agent 1 input
customer_first_name
HubSpot
contact.lastname
Agent 1 input
customer_last_name
HubSpot
contact.account_tier
Agent 1 input
account_tier
HubSpot
contact.hubspot_owner_id
Agent 1 input
owner_id
HubSpot
company.name
Agent 1 input
company_name
HubSpot
company.industry
Agent 1 input
industry
HubSpot
company.annualrevenue
Agent 1 input
annual_revenue
HubSpot
contact.hs_lead_status
Agent 1 input
lead_status
HubSpot
contact.lifecyclestage
Agent 1 input
lifecycle_stage
Orchestration
spend_total_usd
Agent 1 input
spend_total_usd
Orchestration
usage_pct
Agent 1 input
usage_pct
Orchestration
trigger_type
Agent 1 input
trigger_type
Handoff 3: Agent 1 output to Agent 2 (Outreach Drafting Agent)
Source tool
Source field
Destination tool
Destination field
Agent 1 output
offer_id
Agent 2 input
offer_id
Agent 1 output
offer_name
Agent 2 input
offer_name
Agent 1 output
offer_description
Agent 2 input
offer_description
Agent 1 output
selection_rationale
Agent 2 input
selection_rationale
HubSpot (passed through)
customer_email
Agent 2 input
to_email
HubSpot (passed through)
customer_first_name
Agent 2 input
first_name
HubSpot (passed through)
company_name
Agent 2 input
company_name
HubSpot (passed through)
account_tier
Agent 2 input
account_tier
HubSpot (passed through)
owner_id
Agent 2 input
owner_id
Orchestration
trigger_type
Agent 2 input
trigger_type
Orchestration
event_timestamp
Agent 2 input
event_timestamp
Handoff 4: Agent 2 output to HubSpot activity log
Source tool
Source field
Destination tool
Destination field
Agent 2 output
gmail_message_id
HubSpot timeline event
extraData.gmail_message_id
Agent 2 output
send_status
HubSpot timeline event
extraData.send_status
Agent 2 output
offer_name
HubSpot timeline event
extraData.offer_name
Agent 2 output
offer_id
HubSpot timeline event
extraData.offer_id
Orchestration
trigger_type
HubSpot timeline event
extraData.trigger_event
Orchestration
event_timestamp
HubSpot timeline event
timestamp
Orchestration
dedup_key
HubSpot timeline event
extraData.segment_message_id
Handoff 5: Agent 2 output to Google Sheets tracking log
Source tool
Source field
Destination tool
Destination field
Orchestration
event_timestamp
Google Sheets OutcomeLog
timestamp (col A)
HubSpot (passed through)
customer_email
Google Sheets OutcomeLog
customer_email (col B)
HubSpot (passed through)
company_name
Google Sheets OutcomeLog
company_name (col C)
Orchestration
trigger_type
Google Sheets OutcomeLog
trigger_type (col D)
Agent 1 output
offer_name
Google Sheets OutcomeLog
offer_name (col E)
Agent 2 output
send_status
Google Sheets OutcomeLog
send_status (col F)
HubSpot owner lookup
owner_email
Google Sheets OutcomeLog
account_owner_email (col G)
Orchestration
hubspot_contact_link
Google Sheets OutcomeLog
hubspot_link (col H)
Integration and API SpecPage 2 of 3
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
One workflow per agent plus one trigger-and-fetch workflow: Workflow 0 (Segment Trigger and HubSpot Fetch), Workflow 1 (Offer Selection Agent), Workflow 2 (Outreach Drafting Agent). Each workflow is independently versioned and can be paused without affecting others. Workflows are chained sequentially: Workflow 0 calls Workflow 1 on successful data fetch; Workflow 1 calls Workflow 2 on confirmed offer selection.
Shared credential store
A single centralised credential store is used across all workflows. No credentials appear in workflow logic, step configurations, or code nodes. All sensitive values are referenced by key name only.
Workflow 0 trigger mechanism
Webhook (inbound). The orchestration layer exposes an HTTPS endpoint. Segment posts to this endpoint on qualifying events. The workflow validates the HMAC-SHA256 signature using SEGMENT_WEBHOOK_SECRET on every inbound request before any processing begins. Returns HTTP 200 on acceptance, HTTP 401 on signature failure.
Workflow 1 trigger mechanism
Internal call from Workflow 0 (not a webhook or poll). Triggered programmatically when Workflow 0 completes a verified HubSpot data fetch. Receives the full enriched customer payload as its input context.
Workflow 2 trigger mechanism
Internal call from Workflow 1 (not a webhook or poll). Triggered programmatically when Workflow 1 returns a confirmed offer recommendation. Receives the enriched customer payload plus the offer recommendation object.
Idempotency mechanism
Segment messageId is checked against a persistent dedup log (a dedicated Google Sheets tab or a lightweight key-value store in the orchestration platform) at the start of Workflow 0. If the messageId has already been processed, the workflow exits cleanly with no action. New messageId values are written to the dedup log immediately on first receipt.
High-value routing decision
A conditional branch in Workflow 2 evaluates account_tier against the HIGHVALUE_THRESHOLD stored in the credential store. If account_tier equals 'high_value', Gmail draft creation path is taken. Otherwise, Gmail send path is taken. The threshold value must not be hardcoded.
Logging
Each workflow writes a structured execution log entry (workflow name, run ID, input summary, output summary, status, timestamp) to a dedicated Logs tab in the Google Sheets tracking spreadsheet on both success and failure. Failed runs include the error message and the integration point that failed.
Credential store contents (all keys required before first run):
All keys must be present in the credential store before workflow activation. Missing keys must cause an immediate startup error, not a silent failure at runtime.
// Segment
SEGMENT_WEBHOOK_SECRET : string // HMAC-SHA256 shared secret from Segment webhook destination
SEGMENT_WRITE_KEY : string // Segment source write key (for reverse-lookup calls only)
// HubSpot
HUBSPOT_CLIENT_ID : string // OAuth app client ID
HUBSPOT_CLIENT_SECRET : string // OAuth app client secret
HUBSPOT_ACCESS_TOKEN : string // OAuth access token (auto-refreshed)
HUBSPOT_REFRESH_TOKEN : string // OAuth refresh token
HUBSPOT_PORTAL_ID : string // HubSpot account portal ID
HUBSPOT_TIMELINE_EVENT_TYPE_ID : string // Custom timeline event type ID
HUBSPOT_PIPELINE_STAGE_ID : string // Pipeline stage ID for 'Upsell Initiated'
// Gmail
GMAIL_CLIENT_ID : string // Google OAuth client ID
GMAIL_CLIENT_SECRET : string // Google OAuth client secret
GMAIL_ACCESS_TOKEN : string // OAuth access token (auto-refreshed)
GMAIL_REFRESH_TOKEN : string // OAuth refresh token
GMAIL_SENDER_ADDRESS : string // e.g. upsell-outreach@yourcompany.com
GMAIL_DRAFT_LABEL : string // e.g. 'FullSpec-Upsell-Review'
GMAIL_HIGH_VALUE_LABEL : string // e.g. 'FullSpec-HighValue-Sent'
// Slack
SLACK_BOT_TOKEN : string // Bot OAuth token (xoxb-...)
SLACK_FALLBACK_CHANNEL_ID : string // e.g. C0XXXXXXXXX (#sales-alerts channel ID)
SLACK_APP_ID : string // Slack app ID for audit reference
// Google Sheets
GSHEETS_CLIENT_ID : string // Google OAuth client ID
GSHEETS_CLIENT_SECRET : string // Google OAuth client secret
GSHEETS_ACCESS_TOKEN : string // OAuth access token (auto-refreshed)
GSHEETS_REFRESH_TOKEN : string // OAuth refresh token
GSHEETS_SPREADSHEET_ID : string // Master spreadsheet ID
GSHEETS_CATALOGUE_RANGE : string // e.g. 'OfferCatalogue!A2:G'
GSHEETS_TRACKING_RANGE : string // e.g. 'OutcomeLog!A:H'
GSHEETS_DEDUP_RANGE : string // e.g. 'DedupLog!A:B'
// Orchestration rules
HIGHVALUE_THRESHOLD : string // Account tier value that triggers human review
CATALOGUE_CACHE_TTL_SECONDS : number // Max age of cached offer catalogue (max 3600)
05Error handling and retry logic
Unhandled exceptions must never fail silently. Every integration point that raises an error must: (1) log the error with full context to the Logs sheet, (2) post an alert to SLACK_FALLBACK_CHANNEL_ID, and (3) halt the affected workflow run cleanly without partial writes to HubSpot or Google Sheets. Partial state corruption is worse than a missed run.
Integration
Scenario
Required behaviour
Segment inbound webhook
Signature validation fails (HMAC mismatch)
Return HTTP 401 immediately. Do not process payload. Log rejected messageId and source IP. Post alert to SLACK_FALLBACK_CHANNEL_ID. No retry.
Segment inbound webhook
Duplicate messageId received (already processed)
Return HTTP 200 (to prevent Segment retry loop). Log as duplicate. Take no further action. No downstream calls made.
HubSpot contact fetch
Contact not found for userId / email (404)
Halt workflow. Log: event details, lookup field used, timestamp. Post alert to fallback Slack channel. Do not proceed to Agent 1. Manual review required to reconcile Segment userId with HubSpot contact.
HubSpot contact fetch
API rate limit hit (429) or server error (500, 503)
Retry up to 3 times with exponential backoff: 10 seconds, 30 seconds, 90 seconds. If all retries fail, halt workflow, log full error response, post alert to SLACK_FALLBACK_CHANNEL_ID.
Google Sheets catalogue read
Spreadsheet not accessible (403) or range not found (400)
Halt Workflow 1. Do not attempt offer selection with incomplete data. Log error. Post alert to fallback Slack channel. This is a configuration error requiring manual fix before the run can be retried.
Google Sheets catalogue read
API rate limit (429) or transient server error
Retry up to 3 times with exponential backoff: 5 seconds, 15 seconds, 45 seconds. If all retries fail, halt Workflow 1 and log.
Gmail send (standard account)
Send fails (400 bad request, invalid address)
Do not retry. Log failure with to_email and error detail. Create a Gmail draft instead as a fallback so the email is not lost. Log draft creation in HubSpot with send_status = 'send_failed_draft_created'. Post alert to account owner via Slack.
Gmail send (standard account)
API rate limit (429) or server error (500)
Retry up to 3 times with exponential backoff: 10 seconds, 30 seconds, 90 seconds. If all retries fail, fall back to draft creation. Log outcome.
Gmail draft creation (high-value account)
Draft creation fails for any reason
Log full error. Post immediate alert to SLACK_FALLBACK_CHANNEL_ID with full email content (subject, body, recipient) so rep can send manually. Never lose the email content. Retry draft creation once after 60 seconds before falling back to Slack alert.
HubSpot activity log write
Timeline event creation fails (any HTTP error)
Retry up to 3 times with exponential backoff: 10 seconds, 30 seconds, 90 seconds. If all retries fail, log the intended payload to the Google Sheets Logs tab for manual CRM entry. Post alert to SLACK_FALLBACK_CHANNEL_ID. Do not halt the Slack notification or Sheets append steps.
Slack notification
Slack user ID cannot be resolved from owner email
Fall back to posting the notification to SLACK_FALLBACK_CHANNEL_ID with the owner name included in message text. Log the resolution failure. Do not silently skip the notification.
Google Sheets outcome log append
Append fails after all retries (3 attempts, backoff as above)
Write the row payload to the Logs tab as a plaintext record flagged 'PENDING_MANUAL_APPEND'. Post alert to SLACK_FALLBACK_CHANNEL_ID. The tracking sheet must never have silent gaps.
Integration and API SpecPage 3 of 3