Back to Churn Risk Identification

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

Churn Risk Identification

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

This document is the authoritative integration reference for the Churn Risk Identification automation. It covers every external tool the automation connects to, the exact authentication methods and OAuth scopes required, field-level data mappings between agents, the orchestration structure and credential store, and defined error-handling behaviour for every integration point. The FullSpec team uses this specification during build and QA; it also serves as the ongoing technical reference for any future change to the automation.

01Tool inventory

Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
Mixpanel
Product usage signal source: login frequency, event counts, last-seen timestamps
Service account / API secret
Growth (API access required)
Agent 1
Intercom
Support ticket data source: open ticket count, ticket age, unresolved status
OAuth 2.0
Starter or above
Agent 1
HubSpot
CRM record reads, risk tier writes, activity logging, follow-up task creation
OAuth 2.0 (private app token)
Starter CRM or above
Agent 1, Agent 2
Slack
Structured high-risk account alerts posted to a designated channel
Bot token (OAuth 2.0)
Free or above
Agent 2
Gmail
Outreach email draft generation and approved email delivery
OAuth 2.0 (Google Workspace)
Google Workspace (any tier)
Agent 2
Google Sheets
Orchestration run log and audit trail; records each daily run outcome
OAuth 2.0 (Google Workspace)
Google Workspace (any tier)
Agent 1, Agent 2
Orchestration layer
Workflow automation platform that hosts both agent workflows, manages scheduling, credential store, and inter-agent data passing
N/A (internal)
N/A
Both agents
Before you connect anything: every integration must be validated against a sandbox or test environment before production credentials are entered. For Mixpanel, use a test project. For HubSpot, use a sandbox portal. For Intercom, use a test workspace. For Gmail and Google Sheets, use a dedicated service or staging Google Workspace account. Only after all connections return expected data in the test environment should production credentials be loaded into the credential store.
Integration and API SpecPage 1 of 4
FS-DOC-05Technical

02Per-tool integration detail

Mixpanel

Used by Agent 1 (Churn Signal Aggregator) to pull per-account product usage metrics on a daily schedule. Queried via the Mixpanel Query API (JQL or Insights endpoint) to retrieve login frequency, active event counts, and last-seen timestamps for all active accounts.

Auth method
Service account authentication. A Mixpanel service account is created under Project Settings and assigned the Analyst role. The service account username and secret are stored in the credential store. No OAuth flow is required.
Required scopes
Mixpanel service accounts use project-scoped access rather than OAuth scopes. The service account must have read access to the target project. Ensure the project ID is stored in the credential store, not hardcoded.
Trigger / query setup
No inbound webhook. The orchestration layer polls the Mixpanel Query API (https://mixpanel.com/api/2.0/jql or https://data.mixpanel.com/api/2.0/export) each morning on the daily schedule. The query is parameterised by date range (last 14 days rolling) and filters on active user or account identifiers.
Required configuration
MIXPANEL_PROJECT_ID stored in credential store. MIXPANEL_SERVICE_ACCOUNT_USERNAME stored in credential store. MIXPANEL_SERVICE_ACCOUNT_SECRET stored in credential store. Event names used for login and usage scoring must be confirmed against the actual Mixpanel event schema before build (see implementation notes). These event names are stored as environment variables, not hardcoded.
Rate limits
Mixpanel Query API: 60 requests/minute per project for the JQL endpoint; 5 concurrent queries maximum. At current volume (~120 accounts/month, 240 runs/month), the daily batch query is well within limits. A single batched query per run is sufficient; no throttling logic is required at this volume. Throttle if account volume exceeds 500.
Constraints
JQL queries must not span more than 90 days. Event data is available with up to a 24-hour processing lag; the daily schedule should run no earlier than 06:00 in the account's local timezone to ensure previous day's events are available. Mixpanel does not natively expose account-level (company) identifiers unless a group key is configured; confirm group analytics is enabled and the group key matches HubSpot company IDs.
// Query input
project_id: MIXPANEL_PROJECT_ID
date_from: today() - 14d
date_to: today() - 1d
filter: account_id IN [active_account_ids]
// Output fields returned per account
account_id, last_login_date, login_count_14d, key_event_count_14d, days_since_last_event
Intercom

Used by Agent 1 (Churn Signal Aggregator) to retrieve open support ticket data per account. The Conversations API is queried to count open conversations, identify tickets older than 5 days, and count tickets opened in the past 14 days.

Auth method
OAuth 2.0. A dedicated Intercom app is created in the Intercom Developer Hub and authorised against the production workspace. The resulting access token is stored in the credential store as INTERCOM_ACCESS_TOKEN. Token does not expire under the standard integration app flow; refresh logic is not required unless using short-lived tokens.
Required scopes
Read conversations (conversations:read). Read contacts (contacts:read). Read companies (companies:read). Read tags (tags:read).
Trigger / query setup
No inbound webhook. Agent 1 polls the Intercom Conversations API (GET /conversations) with query parameters: state=open, updated_after=[14 days ago]. Results are paginated (page size 150). The automation must follow the pages.next cursor until all pages are consumed.
Required configuration
INTERCOM_ACCESS_TOKEN stored in credential store. INTERCOM_WORKSPACE_ID stored in credential store. Company attribute used to link Intercom conversations to HubSpot company IDs must be confirmed and stored as INTERCOM_COMPANY_ID_FIELD. Ticket age threshold (default 5 days) stored as environment variable TICKET_AGE_THRESHOLD_DAYS.
Rate limits
Intercom REST API: 1,000 requests per minute (Starter and above). Pagination at 150 items per page means ~1 to 2 requests per daily run at current volume. No throttling required. Monitor if account volume grows beyond 2,000 active conversations per run.
Constraints
Intercom Conversations API does not return custom attributes by default; use the include_fields query parameter to request company_id. Archived conversations are excluded by default; no action needed. The Intercom API uses cursor-based pagination; offset pagination is deprecated and must not be used.
// Request
GET /conversations?state=open&updated_after={14_days_ago}&per_page=150
Authorization: Bearer INTERCOM_ACCESS_TOKEN
// Output fields extracted per conversation
conversation_id, company_id, created_at, updated_at, state,
ticket_age_days (derived: today - created_at), open_ticket_count (aggregated by company_id)
HubSpot

Used by both agents. Agent 1 reads account health notes and contact/company records. Agent 2 writes risk tier, triggering signals, and assigned owner back to company records, logs sent email activity, and creates follow-up tasks.

Auth method
OAuth 2.0 via HubSpot private app. A private app is created in HubSpot Settings under Integrations. The private app access token is stored as HUBSPOT_PRIVATE_APP_TOKEN. Private app tokens do not expire; no refresh logic required.
Required scopes
crm.objects.contacts.read. crm.objects.contacts.write. crm.objects.companies.read. crm.objects.companies.write. crm.objects.deals.read. crm.objects.notes.read. crm.objects.notes.write. crm.objects.tasks.write. crm.objects.owners.read. crm.schemas.companies.read.
Webhook / trigger setup
No inbound webhook from HubSpot is required. All HubSpot interactions are outbound API calls from the automation. Agent 1 reads company properties. Agent 2 writes properties and creates engagement objects (notes, tasks).
Required configuration
HUBSPOT_PRIVATE_APP_TOKEN in credential store. HUBSPOT_PORTAL_ID in credential store. Custom company property internal names for risk tier and churn signals must be confirmed before build and stored as environment variables: HUBSPOT_RISK_TIER_PROPERTY, HUBSPOT_CHURN_SIGNALS_PROPERTY, HUBSPOT_LAST_RISK_REVIEW_PROPERTY. Pipeline stage IDs and owner IDs must be fetched via the Owners API at build time and stored as configuration, not hardcoded.
Rate limits
HubSpot private app: 150 requests per 10 seconds (burst); 100 requests per 10 seconds (sustained). Daily run of ~120 accounts requires approximately 400 to 600 API calls (reads, writes, task creation). This is within limits. Implement a 100ms inter-request delay as a precaution. Retry with exponential backoff on 429 responses.
Constraints
Batch API endpoints must be used where possible (PATCH /crm/v3/objects/companies/batch/update) to reduce call count. Custom properties must exist in HubSpot before the automation writes to them; the FullSpec team creates these properties during the build stage. The Engagements API v1 is deprecated; use the CRM Associations API and Notes/Tasks endpoints (v3) only.
// Agent 1 read (company record)
GET /crm/v3/objects/companies/{id}?properties=name,hs_lastcontacteddate,
  hubspot_owner_id,churn_risk_tier,health_score_notes
// Agent 2 write (batch update risk tier)
PATCH /crm/v3/objects/companies/batch/update
  { inputs: [{ id, properties: { churn_risk_tier, churn_signals, last_risk_review_date } }] }
// Agent 2 create task
POST /crm/v3/objects/tasks
  { properties: { hs_task_subject, hs_task_body, hs_task_due_date, hubspot_owner_id } }
// Agent 2 log email activity (note)
POST /crm/v3/objects/notes
  { properties: { hs_note_body, hs_timestamp }, associations: [{ company_id }] }
Slack

Used by Agent 2 (Risk Scoring and Routing Agent) to post structured high-risk account alerts to a designated channel. Each alert includes account name, risk tier, triggering signals, and the assigned CSM.

Auth method
Bot token (OAuth 2.0). A Slack app is created at api.slack.com/apps and installed to the workspace. The bot token (xoxb-...) is stored as SLACK_BOT_TOKEN in the credential store. The app requires the Bot Token Scopes listed below.
Required scopes
chat:write. chat:write.public (if the bot is not a member of the target channel). channels:read. users:read (to resolve CSM Slack user IDs from email addresses for @mentions).
Webhook / trigger setup
Outbound only. No inbound Slack events or webhooks are required for the current build. The automation calls the Slack Web API chat.postMessage method. The target channel ID is stored as SLACK_CHURN_ALERT_CHANNEL_ID in the credential store, not the channel name (names can change; IDs cannot).
Required configuration
SLACK_BOT_TOKEN in credential store. SLACK_CHURN_ALERT_CHANNEL_ID in credential store (retrieve via conversations.list at build time). CSM Slack user ID map (keyed by HubSpot owner ID or email) stored as a configuration lookup table in the credential store or as a JSON environment variable SLACK_CSM_USER_ID_MAP.
Rate limits
Slack Web API: Tier 3 rate limit for chat.postMessage: 1 request per second sustained, burst tolerated. At current volume (~120 accounts/day, subset flagged as high risk), alert volume is well under the limit. No throttling required. Add a 1-second delay between consecutive postMessage calls if more than 10 high-risk accounts are alerted in a single run.
Constraints
Block Kit is the required format for structured alerts; legacy attachments are deprecated. Message payloads must not exceed 3,000 characters per block. The bot must be invited to the target channel before alerts can be posted; this is a manual step completed during the launch stage.
// chat.postMessage payload
POST https://slack.com/api/chat.postMessage
Authorization: Bearer SLACK_BOT_TOKEN
{
  channel: SLACK_CHURN_ALERT_CHANNEL_ID,
  blocks: [
    { type: 'header', text: { type: 'plain_text', text: ':warning: High-Risk Account: {account_name}' } },
    { type: 'section', fields: [
        { type: 'mrkdwn', text: '*Risk tier:* High' },
        { type: 'mrkdwn', text: '*Assigned CSM:* <@{csm_slack_user_id}>' },
        { type: 'mrkdwn', text: '*Signals:* {churn_signals_summary}' },
        { type: 'mrkdwn', text: '*Last contact:* {last_contacted_date}' }
    ]}
  ]
}
Gmail

Used by Agent 2 (Risk Scoring and Routing Agent) to generate a pre-drafted outreach email and, after CSM approval, to send the personalised message to the at-risk customer. Authentication is via the Google Workspace account of the sending CSM or a shared mailbox.

Auth method
OAuth 2.0 via a Google Cloud project. A service account with domain-wide delegation is recommended for a shared outreach mailbox. If per-CSM sending is required, individual OAuth tokens per CSM must be stored. The access token or service account credentials are stored as GMAIL_SERVICE_ACCOUNT_JSON in the credential store.
Required scopes
https://www.googleapis.com/auth/gmail.send. https://www.googleapis.com/auth/gmail.compose. https://www.googleapis.com/auth/gmail.readonly (for monitoring reply status in future phases).
Webhook / trigger setup
Outbound only for current build. Email is sent via the Gmail API users.messages.send endpoint after the CSM approval signal is received. The approval mechanism is a Slack interactive button or a dedicated approval step within the orchestration platform; the exact implementation is confirmed during build. Gmail push notifications (inbound webhook via Pub/Sub) are not required for this phase.
Required configuration
GMAIL_SERVICE_ACCOUNT_JSON in credential store (if using service account). GMAIL_SENDING_ADDRESS stored as GMAIL_FROM_ADDRESS in credential store. Email template IDs or template strings stored as environment variables: GMAIL_OUTREACH_TEMPLATE_HIGH_RISK. Template placeholders: {{account_name}}, {{csm_first_name}}, {{last_login_days}}, {{open_ticket_count}}, {{company_name}}, {{csm_signature}}. These placeholder keys must exactly match the field names output by Agent 2.
Rate limits
Gmail API: 250 quota units per second per user; users.messages.send costs 100 units per call, allowing approximately 2 sends per second per user. At current volume, a maximum of ~120 emails per day is expected (most accounts will not be high risk on any given day; typical high-risk volume is estimated at 10 to 20 per day). No throttling required. Add a 500ms delay between send calls if more than 20 emails are queued in a single run.
Constraints
Emails must be constructed as RFC 2822 MIME messages and base64url-encoded before submission to the API. HTML email body is supported; plain text fallback must be included as a multipart/alternative part. DKIM and SPF records for the sending domain must be verified in Google Workspace before launch to avoid deliverability issues. The CSM approval step must complete before the send call is made; the automation must not send without an explicit approval signal.
// users.messages.send payload (simplified)
POST https://gmail.googleapis.com/gmail/v1/users/{from_address}/messages/send
Authorization: Bearer {access_token}
{
  raw: base64url(
    From: {GMAIL_FROM_ADDRESS}
    To: {customer_email}
    Subject: {email_subject}
    MIME-Version: 1.0
    Content-Type: multipart/alternative
    // HTML part: rendered template with placeholders substituted
    // Plain text part: stripped version of HTML body
  )
}
Google Sheets

Used by both agents as the orchestration run log and audit trail. Each daily run appends a row recording the run timestamp, accounts processed, risk tiers assigned, alerts sent, and any errors encountered. This provides a human-readable audit trail without querying the automation platform logs directly.

Auth method
OAuth 2.0 via the same Google Cloud project used for Gmail. The service account (GMAIL_SERVICE_ACCOUNT_JSON) is granted Editor access to the target Google Sheet. No separate credential is required.
Required scopes
https://www.googleapis.com/auth/spreadsheets. https://www.googleapis.com/auth/drive.file (scoped to the specific sheet only, not all of Drive).
Webhook / trigger setup
No webhook. The automation appends rows via the Sheets API spreadsheets.values.append method at the end of each agent run. No inbound trigger from Sheets is used.
Required configuration
GOOGLE_SHEETS_LOG_SPREADSHEET_ID stored in credential store. Sheet tab name for the run log stored as SHEETS_LOG_TAB_NAME (default: 'RunLog'). Sheet tab name for the account snapshot stored as SHEETS_SNAPSHOT_TAB_NAME (default: 'AccountSnapshot'). Spreadsheet column structure must be pre-created (headers in row 1) before the automation first runs: run_date, accounts_processed, high_risk_count, medium_risk_count, low_risk_count, alerts_sent, emails_sent, errors, run_duration_seconds.
Rate limits
Google Sheets API: 300 read and 300 write requests per minute per project. Each daily run performs 1 to 3 append operations. Well within limits; no throttling required.
Constraints
The spreadsheet must not be used for active business data entry during automation runs to avoid range conflicts. Row append targets the first empty row after the last populated row; the range must be specified as a column range (e.g. A:M) rather than a fixed cell reference to allow unbounded appends.
// Append run log row
POST https://sheets.googleapis.com/v4/spreadsheets/{SPREADSHEET_ID}/values/{SHEETS_LOG_TAB_NAME}!A:M:append
  ?valueInputOption=USER_ENTERED&insertDataOption=INSERT_ROWS
  { values: [[run_date, accounts_processed, high_risk_count, medium_risk_count,
               low_risk_count, alerts_sent, emails_sent, errors, run_duration_seconds]] }
Integration and API SpecPage 2 of 4
FS-DOC-05Technical

03Field mappings between tools

The following tables define the exact field mappings at each agent handoff point. All field names are shown in their API-exact form. Where a field is derived or computed by the agent rather than passed directly from a source, this is noted in the destination field column.

Handoff 1: Mixpanel, Intercom, and HubSpot to Agent 1 (Churn Signal Aggregator) account snapshot output.

Source tool
Source field
Destination tool
Destination field
Mixpanel
`account_id`
Account snapshot
`account_id`
Mixpanel
`last_event_timestamp`
Account snapshot
`last_login_date` (derived: ISO date from epoch ms)
Mixpanel
`event_count` (14d window)
Account snapshot
`login_count_14d`
Mixpanel
`key_action_count` (14d window)
Account snapshot
`key_event_count_14d`
Mixpanel
`last_event_timestamp`
Account snapshot
`days_since_last_event` (derived: today - last_event_timestamp)
Intercom
`company.company_id`
Account snapshot
`account_id` (join key)
Intercom
`conversation.created_at`
Account snapshot
`oldest_open_ticket_age_days` (derived: max age across open conversations)
Intercom
count of `conversation.state == open`
Account snapshot
`open_ticket_count`
Intercom
count where `created_at >= today - 14d`
Account snapshot
`tickets_opened_14d`
HubSpot
`hs_lastcontacteddate`
Account snapshot
`last_contacted_date`
HubSpot
`hubspot_owner_id`
Account snapshot
`csm_owner_id`
HubSpot
`name` (company)
Account snapshot
`company_name`
HubSpot
`hs_object_id` (company)
Account snapshot
`hubspot_company_id`
HubSpot
custom: `churn_risk_health_score`
Account snapshot
`existing_health_score`

Handoff 2: Agent 1 account snapshot to Agent 2 (Risk Scoring and Routing Agent) inputs.

Source tool
Source field
Destination tool
Destination field
Account snapshot
`account_id`
Agent 2 context
`account_id`
Account snapshot
`company_name`
Agent 2 context
`company_name`
Account snapshot
`hubspot_company_id`
Agent 2 context
`hubspot_company_id`
Account snapshot
`csm_owner_id`
Agent 2 context
`csm_owner_id`
Account snapshot
`login_count_14d`
Agent 2 scoring
`signal_login_count`
Account snapshot
`days_since_last_event`
Agent 2 scoring
`signal_days_inactive`
Account snapshot
`open_ticket_count`
Agent 2 scoring
`signal_open_tickets`
Account snapshot
`oldest_open_ticket_age_days`
Agent 2 scoring
`signal_max_ticket_age`
Account snapshot
`tickets_opened_14d`
Agent 2 scoring
`signal_ticket_volume_14d`
Account snapshot
`last_contacted_date`
Agent 2 scoring
`signal_days_since_contact` (derived)
Account snapshot
`existing_health_score`
Agent 2 scoring
`signal_health_score`

Handoff 3: Agent 2 outputs to HubSpot, Slack, and Gmail.

Source tool
Source field
Destination tool
Destination field
Agent 2 output
`risk_tier`
HubSpot
custom: `churn_risk_tier` (company property)
Agent 2 output
`churn_signals_summary`
HubSpot
custom: `churn_signals` (company property, text)
Agent 2 output
`run_date`
HubSpot
custom: `last_risk_review_date` (company property, date)
Agent 2 output
`csm_owner_id`
HubSpot
`hubspot_owner_id` (company property, unchanged if already set)
Agent 2 output
`email_draft_html`
HubSpot note
`hs_note_body` (stored as draft pending approval)
Agent 2 output
`follow_up_due_date`
HubSpot task
`hs_task_due_date`
Agent 2 output
`task_subject`
HubSpot task
`hs_task_subject`
Agent 2 output
`company_name`
Slack alert
Block Kit header: `{account_name}`
Agent 2 output
`risk_tier`
Slack alert
Block Kit field: `*Risk tier:*`
Agent 2 output
`churn_signals_summary`
Slack alert
Block Kit field: `*Signals:*`
Agent 2 output
`csm_slack_user_id`
Slack alert
Block Kit field: `<@{csm_slack_user_id}>`
Agent 2 output
`customer_email`
Gmail send
`To:` header
Agent 2 output
`email_subject`
Gmail send
`Subject:` header
Agent 2 output
`email_draft_html`
Gmail send
MIME HTML part body
Agent 2 output
`email_draft_plaintext`
Gmail send
MIME plain text part body
Integration and API SpecPage 3 of 4
FS-DOC-05Technical

04Build stack and orchestration

Orchestration layout
Two discrete workflows: one per agent. Workflow 1 (Churn Signal Aggregator) runs on a daily scheduled trigger. Workflow 2 (Risk Scoring and Routing Agent) is triggered by the completion event of Workflow 1, consuming the account snapshot payload passed via the shared data store or inter-workflow call. Both workflows share a single credential store scoped to this process.
Agent 1 trigger mechanism
Scheduled poll. The orchestration platform fires Workflow 1 at a configured time each morning (e.g. 06:30 in the customer's local timezone). No inbound webhook is involved. The trigger passes a run_date parameter and the list of active account IDs to be processed in that run.
Agent 2 trigger mechanism
Chained execution (event-based). Agent 2 is triggered immediately upon successful completion of Agent 1. The account snapshot payload (JSON array of normalised account objects) is passed as the trigger input. If Agent 1 fails, Agent 2 does not fire; the failure is logged and the on-call notification is sent.
CSM approval step trigger
The Slack alert posted by Agent 2 includes an interactive approval button (Block Kit actions component). When the CSM clicks Approve, a Slack action event is sent to the orchestration platform's inbound webhook URL. The platform validates the request signature using the Slack signing secret before proceeding to the Gmail send step. If the CSM clicks Edit, the workflow pauses and the CSM completes the edit and send manually.
Slack signature validation
All inbound Slack action payloads must be validated against the X-Slack-Signature header using HMAC-SHA256 with the SLACK_SIGNING_SECRET. Requests failing signature validation are rejected with HTTP 401 and logged. This is a mandatory security requirement; do not skip in staging or production.
Shared credential store
All secrets are stored in the orchestration platform's built-in encrypted credential store. No secrets appear in workflow node configuration, environment files, or logs. Credential references in workflow nodes use the variable names listed in the code block below.
Inter-agent data passing
Agent 1 writes the full account snapshot as a JSON array to a shared workflow variable or the orchestration platform's inter-workflow data bus. Agent 2 reads this payload as its primary input. The snapshot is also appended to the Google Sheets audit log as a human-readable record.
Run log
Both agents append a summary row to the Google Sheets run log (SHEETS_LOG_TAB_NAME) at the end of each execution. Errors are written to the errors column; a blank errors cell indicates a clean run.
Credential store variable reference: all names used in workflow node configuration
// Credential store contents
// All values are encrypted at rest in the orchestration platform credential store.
// Reference by variable name in workflow nodes; never hardcode values.

MIXPANEL_PROJECT_ID              = "<mixpanel_project_id>"
MIXPANEL_SERVICE_ACCOUNT_USERNAME = "<service_account_username>"
MIXPANEL_SERVICE_ACCOUNT_SECRET   = "<service_account_secret>"
MIXPANEL_EVENT_LOGIN              = "<confirmed_login_event_name>"
MIXPANEL_EVENT_KEY_ACTION         = "<confirmed_key_action_event_name>"

INTERCOM_ACCESS_TOKEN             = "<intercom_oauth_access_token>"
INTERCOM_WORKSPACE_ID             = "<intercom_workspace_id>"
INTERCOM_COMPANY_ID_FIELD         = "<company_attribute_name_for_account_id>"
TICKET_AGE_THRESHOLD_DAYS         = "5"

HUBSPOT_PRIVATE_APP_TOKEN         = "<hubspot_private_app_access_token>"
HUBSPOT_PORTAL_ID                 = "<hubspot_portal_id>"
HUBSPOT_RISK_TIER_PROPERTY        = "churn_risk_tier"
HUBSPOT_CHURN_SIGNALS_PROPERTY    = "churn_signals"
HUBSPOT_LAST_RISK_REVIEW_PROPERTY = "last_risk_review_date"

SLACK_BOT_TOKEN                   = "xoxb-<bot_token>"
SLACK_CHURN_ALERT_CHANNEL_ID      = "<channel_id>"
SLACK_SIGNING_SECRET              = "<slack_app_signing_secret>"
SLACK_CSM_USER_ID_MAP             = "{\"<hubspot_owner_id>\": \"<slack_user_id>\", ...}"

GMAIL_SERVICE_ACCOUNT_JSON        = "<base64_encoded_service_account_key_json>"
GMAIL_FROM_ADDRESS                = "<outreach_sending_address>"
GMAIL_OUTREACH_TEMPLATE_HIGH_RISK = "<html_template_string_or_template_id>"

GOOGLE_SHEETS_LOG_SPREADSHEET_ID  = "<spreadsheet_id>"
SHEETS_LOG_TAB_NAME               = "RunLog"
SHEETS_SNAPSHOT_TAB_NAME          = "AccountSnapshot"

// Scoring thresholds (configurable; adjust after first 30-day review)
SCORE_LOGIN_DROP_THRESHOLD_DAYS   = "10"
SCORE_OPEN_TICKET_HIGH            = "3"
SCORE_TICKET_AGE_HIGH_DAYS        = "5"
SCORE_DAYS_NO_CONTACT_HIGH        = "30"
SCORE_HIGH_RISK_TOTAL_THRESHOLD   = "70"
SCORE_MEDIUM_RISK_TOTAL_THRESHOLD = "40"

05Error handling and retry logic

Unhandled exceptions must never fail silently. Every error path must write to the Google Sheets run log, trigger an alert to the designated operations Slack channel (separate from the churn alert channel), and halt the affected workflow branch cleanly. A silent failure that causes a daily run to produce no output is indistinguishable from a clean run with no high-risk accounts and must be prevented.
Integration
Scenario
Required behaviour
Mixpanel API
HTTP 401 Unauthorized (invalid service account credentials)
Halt Workflow 1 immediately. Log error to Sheets run log with error code and timestamp. Post alert to ops Slack channel. Do not proceed to Agent 2. No retry; credential rotation is required before next run.
Mixpanel API
HTTP 429 Rate limit exceeded
Wait 60 seconds, then retry the failed request up to 3 times with exponential backoff (60s, 120s, 240s). If all retries fail, log to Sheets and post ops Slack alert. Proceed with partial data if at least 80% of accounts returned successfully; otherwise halt.
Mixpanel API
Query returns no data for expected date range (possible processing lag)
Log warning to Sheets. Retry query once with date range extended by 1 day. If still empty, post ops Slack alert noting possible Mixpanel data lag and continue run using last available snapshot for affected accounts, flagging them as stale_data in the account snapshot.
Intercom API
HTTP 401 Unauthorized (expired or revoked token)
Halt Workflow 1. Log to Sheets. Post ops Slack alert. Do not proceed. Token must be refreshed and re-stored in the credential store before next scheduled run.
Intercom API
Pagination cursor expires mid-run
Log the last consumed page number to Sheets. Restart the Intercom query from the beginning for the affected date range. Retry up to 2 times. If pagination consistently fails, process accounts with ticket data already retrieved and flag the remainder as incomplete_ticket_data in the snapshot.
HubSpot API
HTTP 429 Rate limit (burst or sustained)
Pause execution for 10 seconds, then retry the failed request up to 5 times with exponential backoff (10s, 20s, 40s, 80s, 160s). If all retries fail, log affected company IDs to Sheets as write_failed and post ops Slack alert. Continue processing remaining accounts.
HubSpot API
Property does not exist (custom property not yet created)
Halt Workflow 2 write step for that property. Log to Sheets with property name and affected company ID. Post ops Slack alert. This indicates a pre-launch configuration error; the custom property must be created in HubSpot before the automation can write to it.
HubSpot API
Task or note creation fails for a specific company record
Log the failure to Sheets with company ID and error detail. Continue processing remaining records. Post a summary of failed task creations in the ops Slack alert at end of run. Manual follow-up is required for failed records.
Slack
Bot token invalid or app not installed to workspace
Log error to Sheets. Halt alert posting step. Post email notification to support@gofullspec.com as a fallback alert mechanism. Do not proceed to Gmail send step without CSM awareness of high-risk accounts.
Slack
Channel ID not found (channel deleted or renamed)
Log error to Sheets. Attempt to post to a fallback channel ID stored as SLACK_OPS_FALLBACK_CHANNEL_ID. If fallback also fails, send email alert to CSM team distribution list as manual fallback.
Gmail
OAuth token expired or revoked (service account delegation issue)
Halt email send step. Log to Sheets. Post ops Slack alert with list of accounts awaiting outreach. CSM team must send emails manually from their Gmail accounts. FullSpec team is notified at support@gofullspec.com to resolve credential issue.
Gmail
Send fails for a specific recipient (invalid address or delivery error)
Log the failure to Sheets with recipient address and error code. Continue sending to remaining approved recipients. Include failed sends in the end-of-run ops Slack summary. CSM must follow up manually for failed addresses.
Google Sheets
Append fails (spreadsheet ID invalid or permissions revoked)
Log error to the orchestration platform's internal error log. Post ops Slack alert noting that the audit log append failed. Continue the main workflow; the audit log failure must not block churn alerting or email sending. Investigate permissions at the next maintenance window.
Orchestration layer
Agent 1 completes but produces zero account records (unexpected empty output)
Do not trigger Agent 2. Log to Sheets. Post ops Slack alert. An empty account list on a normal business day indicates a data connection failure upstream, not a legitimate result. Treat as an error requiring investigation.
Orchestration layer
Agent 2 receives CSM approval signal but Slack action signature validation fails
Reject the action. Return HTTP 401. Log the rejected request with IP address and timestamp to Sheets. Do not send the email. Post ops Slack alert noting a rejected or invalid approval attempt.
Integration and API SpecPage 4 of 4

More documents for this process

Every document generated for Churn Risk Identification.

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