FS-DOC-05Technical
Integration and API Spec
Influencer & Partnership Outreach Automation
[YourCompany.com] · Marketing Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for every external service connected in the Influencer and Partnership Outreach automation. It covers authentication methods, required OAuth scopes, webhook setup, field mappings between tools, orchestration layout, credential-store contents, and failure handling. The FullSpec team uses this spec to build, configure, and maintain every integration. Your team uses it to understand what is connected, why, and how failures are surfaced.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Automation platform (orchestration layer)
Hosts all workflows; polls and receives webhooks; executes agent logic; manages credential store
Internal (platform-native)
Per platform pricing; no external plan required
All agents
Notion
Campaign brief capture and trigger source; criteria storage read by Prospect Research Agent
OAuth 2.0 (internal integration token)
Free tier sufficient; Integration token requires workspace admin access
Agent 1 (Prospect Research)
Google Sheets
Prospect list staging; enrichment output target; send-status logging
OAuth 2.0 (service account or user OAuth)
Google Workspace free tier sufficient
Agent 1 (Prospect Research), Agent 2 (Pitch Drafting)
Hunter.io
Email address lookup and verification per prospect row
API key (Bearer token in Authorization header)
Starter ($49/month); 500 searches/month minimum
Agent 1 (Prospect Research)
HubSpot
Contact record creation; campaign tagging; deal stage management; follow-up sequence enrolment
OAuth 2.0 (private app token)
Sales Hub Starter ($50/month); Sequences require paid tier
Agent 2 (Pitch Drafting), Agent 3 (Follow-Up and Pipeline)
Gmail
Draft creation for human review; approved pitch sending; reply-status monitoring
OAuth 2.0 (user-level; Google Identity Platform)
Google Workspace (any paid tier); free Gmail not recommended for business sending
Agent 2 (Pitch Drafting), Agent 3 (Follow-Up and Pipeline)
Slack
Hot lead alert delivery to marketing channel
OAuth 2.0 (bot token via Slack App)
Free tier sufficient for incoming webhooks
Agent 3 (Follow-Up and Pipeline)
Before you connect anything: sandbox-test all connections using non-production credentials before introducing live API keys or production OAuth tokens. Create a dedicated test workspace in Notion, a test Google Sheet, a sandboxed HubSpot portal, and a private Slack channel. Validate each integration returns expected payloads before wiring agents end to end. Never hardcode production credentials in workflow nodes; all secrets go into the credential store only.
Integration and API SpecPage 1 of 4
FS-DOC-05Technical
02Per-tool integration detail
Notion
Notion serves as the campaign brief capture point. The automation platform listens for a page-updated event on the designated campaign briefs database. When a page's Status property transitions to 'Ready', the platform reads the brief fields and passes them to the Prospect Research Agent. The Notion internal integration token must be scoped to the specific database; do not grant workspace-wide access.
Auth method
OAuth 2.0 internal integration token. Generate via Settings > Connections > Develop or manage integrations. Token stored in credential store as NOTION_API_TOKEN.
Required scopes
read_content, read_user (workspace-level read is sufficient; write access is not required for this integration)
Webhook / trigger setup
Notion does not offer native outbound webhooks. The orchestration layer polls the campaign briefs database on a 5-minute interval using GET /v1/databases/{database_id}/query with a filter on the Status property equal to 'Ready' and last_edited_time greater than the last successful run timestamp. Store the last_poll_timestamp in the platform's run-state variable between executions.
Required configuration
NOTION_CAMPAIGN_DB_ID stored in credential store. Brief template must contain the following properties: Campaign Name (title), Target Niche (rich_text), Min Audience Size (number), Max Audience Size (number), Channel (select: Instagram/YouTube/Podcast/Blog), Status (select: Draft/Ready/In Progress/Complete), Brief Owner (people). Do not hardcode the database ID.
Rate limits
Notion API: 3 requests/second per integration. At 40 contacts/week with a 5-minute poll cycle, average throughput is well below this ceiling. Throttling is not required at current volume; implement a 400ms inter-request delay as a precaution.
Constraints
The Status property must be a Notion select field, not a formula or rollup. The integration token must be explicitly shared with the database by a workspace admin. Pages in Notion sub-pages or linked databases will not be detected unless the integration is shared with those pages individually.
// Output to Prospect Research Agent
campaign_name: string
target_niche: string
min_audience_size: integer
max_audience_size: integer
channel: enum(Instagram|YouTube|Podcast|Blog)
brief_owner_email: string
notion_page_id: string // retained for status write-back
Google Sheets
Google Sheets acts as the prospect staging layer. The Prospect Research Agent writes one row per prospect with enrichment data. The Pitch Drafting Agent reads those rows to construct personalised draft emails. A dedicated sheet per campaign is required; the orchestration layer references the sheet by its stored spreadsheet ID, not by tab name alone.
Auth method
OAuth 2.0 using a Google service account with a downloaded JSON key, OR user-level OAuth with offline access token. Service account is preferred for unattended execution. Credential stored as GOOGLE_SERVICE_ACCOUNT_JSON or GOOGLE_OAUTH_REFRESH_TOKEN.
Required scopes
https://www.googleapis.com/auth/spreadsheets (read and write to sheets); https://www.googleapis.com/auth/drive.file (create new sheets if required per campaign)
Webhook / trigger setup
No webhook. The Pitch Drafting Agent polls the sheet on a 10-minute interval for rows where email_verified equals TRUE and draft_status equals PENDING. The Prospect Research Agent appends rows using spreadsheets.values.append on the designated range.
Required configuration
GOOGLE_SHEET_TEMPLATE_ID stored in credential store. Each campaign creates a copy of the template sheet; the new sheet ID is written to the Notion page as a property. Column headers must match exactly: prospect_name, handle, niche, audience_size_est, channel, email_address, email_verified, hunter_confidence, draft_status, sent_timestamp, hubspot_contact_id, hubspot_deal_id.
Rate limits
Google Sheets API: 300 read requests/minute, 300 write requests/minute per project. At 40 rows/campaign batch with 2 agents reading and writing, peak load is approximately 80 requests/minute. Throttling not required; implement exponential backoff on 429 responses.
Constraints
Rows must not be deleted during an active agent run; use a status column to mark rows as processed. Shared drives require additional Drive API scope. Do not use merged cells in the prospect sheet as they break range reads.
// Input from Prospect Research Agent (append)
prospect_name, handle, niche, audience_size_est, channel, email_address, email_verified, hunter_confidence
// Output read by Pitch Drafting Agent
prospect_name, email_address, niche, handle, campaign_name, channel
// Written back by Pitch Drafting Agent
draft_status: enum(PENDING|DRAFTED|APPROVED|SENT)
hubspot_contact_id: string
sent_timestamp: ISO8601
Hunter.io
Hunter.io provides email address lookup and confidence scoring for each prospect. The Prospect Research Agent calls the Email Finder endpoint per prospect using the prospect's full name and domain derived from their social handle or website. Results with a confidence score below 70 are flagged for manual review rather than passed downstream.
Auth method
API key passed as query parameter api_key={key} on every request. Stored in credential store as HUNTER_API_KEY. No OAuth flow required.
Required scopes
N/A (API key is account-level; no scope granularity available). Ensure the account plan has sufficient monthly search quota.
Webhook / trigger setup
No webhook. Hunter.io is called synchronously per prospect row during the Prospect Research Agent run. Endpoint: GET https://api.hunter.io/v2/email-finder?domain={domain}&first_name={first}&last_name={last}&api_key={key}
Required configuration
HUNTER_API_KEY in credential store. HUNTER_CONFIDENCE_THRESHOLD set to 70 (integer, stored as platform environment variable, not hardcoded). Domain extraction logic must strip @ prefixes and resolve vanity domains to root domains before calling the API.
Rate limits
Starter plan: 500 searches/month, 10 requests/second. At 40 contacts/week (approximately 160/month), volume is safely within the 500/month quota. Insert a 150ms delay between sequential calls to stay within the 10 req/s ceiling. If volume exceeds 400 searches/month, alert the process owner to upgrade the plan.
Constraints
Hunter.io cannot reliably find emails for individual creators with no associated domain (e.g. TikTok-only accounts with no website). Rows where the API returns status: 'email_not_found' or confidence below threshold must be written to the sheet with email_verified: FALSE and flagged in a separate 'Needs Manual Review' tab. Never pass unverified emails to the Pitch Drafting Agent.
// Request
GET /v2/email-finder
domain: string // e.g. 'janedoe.com'
first_name: string
last_name: string
api_key: HUNTER_API_KEY
// Response fields used
data.email: string
data.score: integer // confidence 0-100
data.status: enum(valid|accept_all|webmail|unknown)
// Written to Google Sheet
email_address: data.email
hunter_confidence: data.score
email_verified: boolean // true if score >= HUNTER_CONFIDENCE_THRESHOLD
HubSpot
HubSpot is used in two stages: the Pitch Drafting Agent creates contact records and tags them with campaign metadata; the Follow-Up and Pipeline Agent manages sequence enrolment, deal stage transitions, and monitors reply events. A private app token is used throughout. Sequences require Sales Hub Starter or higher.
Auth method
Private app token (Bearer token). Generate via HubSpot Settings > Integrations > Private Apps. Stored as HUBSPOT_PRIVATE_APP_TOKEN in credential store. Do not use legacy API keys; they are deprecated.
Required scopes
crm.objects.contacts.read, crm.objects.contacts.write, crm.objects.deals.read, crm.objects.deals.write, crm.schemas.contacts.read, sales-email-read, crm.objects.marketing_events.write, automation, timeline, e-commerce (optional for future reporting)
Webhook / trigger setup
HubSpot webhooks are configured via Settings > Integrations > Webhooks. Subscribe to: contact.propertyChange (property: hs_email_last_replied_date) and deal.propertyChange (property: dealstage). The orchestration layer exposes a public HTTPS endpoint to receive these events. Validate the X-HubSpot-Signature-v3 header using HMAC-SHA256 with the client secret stored as HUBSPOT_WEBHOOK_SECRET. Reject any request where the signature does not match.
Required configuration
HUBSPOT_PORTAL_ID in credential store. HUBSPOT_CAMPAIGN_TAG_PROPERTY set to the internal name of the custom contact property used for campaign tagging (e.g. 'campaign_tag'). HUBSPOT_PIPELINE_ID and HUBSPOT_SEQUENCE_ID stored as platform variables, not hardcoded. Pipeline stages required: Prospect Identified, Pitch Drafted, Pitch Sent, Replied, In Negotiation, Declined, No Response. Create these in HubSpot before build starts.
Rate limits
HubSpot API: 100 requests/10 seconds, 150,000 requests/day on paid plans. At 40 contacts/week with 3-4 API calls per contact, peak batch volume is approximately 160 calls per run, well within limits. No throttling required. Implement retry with backoff on 429 responses.
Constraints
Sequence enrolment via API requires the enrolled contact to have a valid email address on the contact record. Contacts already enrolled in another active sequence cannot be enrolled again without unenrolling first. The Follow-Up and Pipeline Agent must check sequence_enrolment_status before calling the enrol endpoint. Deal stage IDs are portal-specific and must be fetched via GET /crm/v3/pipelines/deals/{pipelineId} and stored, not hardcoded.
// Contact creation (Agent 2)
POST /crm/v3/objects/contacts
email, firstname, lastname
campaign_tag: string
channel: string
niche: string
hs_lead_status: 'NEW'
// Deal stage update (Agent 3)
PATCH /crm/v3/objects/deals/{dealId}
dealstage: HUBSPOT_STAGE_ID
// Sequence enrolment (Agent 3)
POST /automation/v4/sequences/enroll
sequenceId: HUBSPOT_SEQUENCE_ID
contactId: string
senderEmail: stringGmail
Gmail is used by the Pitch Drafting Agent to save personalised draft emails for human review, and by the Follow-Up and Pipeline Agent to detect replies and mark sent emails. The marketing manager's Gmail account is the sending account. OAuth must be granted by the account owner, not a service account, because drafts must appear in the manager's own Gmail interface.
Auth method
OAuth 2.0 (user-level). The marketing manager completes the OAuth consent flow; the resulting refresh token is stored as GMAIL_OAUTH_REFRESH_TOKEN in the credential store. Access tokens are refreshed automatically before each agent run.
Required scopes
https://www.googleapis.com/auth/gmail.compose (create and modify drafts); https://www.googleapis.com/auth/gmail.send (send approved drafts); https://www.googleapis.com/auth/gmail.readonly (read reply status and thread metadata); https://www.googleapis.com/auth/gmail.modify (apply labels, mark as read)
Webhook / trigger setup
Gmail push notifications via Google Cloud Pub/Sub: call users.watch with topicName set to the registered Pub/Sub topic (GMAIL_PUBSUB_TOPIC stored in credential store). The orchestration layer subscribes to this topic to receive new-message notifications. Re-call users.watch every 6 days to renew the watch subscription (it expires after 7 days). On notification receipt, call users.messages.get to retrieve the full thread and check In-Reply-To header against sent message IDs.
Required configuration
GMAIL_SENDING_ADDRESS stored in credential store. GMAIL_DRAFT_LABEL set to 'Outreach-Pending-Review' (create this label in Gmail before build). GMAIL_SENT_FOLDER_ID not required; use the SENT system label. Google Cloud project with Gmail API and Pub/Sub API enabled; GOOGLE_CLOUD_PROJECT_ID in credential store.
Rate limits
Gmail API: 250 quota units/user/second; draft creation costs 10 units, send costs 100 units. At 40 drafts/week sent in a single batch, peak cost is 40 x 100 = 4,000 units spread over the batch duration. No throttling required; implement a 1-second delay between send calls to avoid bursting.
Constraints
Drafts created via API appear in the manager's Gmail Drafts folder and carry the OUTREACH-PENDING-REVIEW label. The automation must not call users.messages.send autonomously; the approved draft is sent only after the manager triggers send from Gmail UI or via an explicit API call following confirmation. Thread ID must be retained per contact to correlate replies accurately. OAuth token must be re-authorised if the refresh token is revoked (e.g. after a Google account password reset).
// Draft creation (Agent 2)
POST /gmail/v1/users/me/drafts
message.raw: base64url(MIME email with To, Subject, Body)
// Label applied post-creation:
POST /gmail/v1/users/me/messages/{draftMsgId}/modify
addLabelIds: ['OUTREACH-PENDING-REVIEW']
// Reply detection (Agent 3)
GET /gmail/v1/users/me/threads/{threadId}
// Check messages[].labelIds for absence of SENT
// Check In-Reply-To or References headersSlack
Slack receives hot lead alert messages posted by the Follow-Up and Pipeline Agent when a prospect replies or clicks through. A dedicated Slack App with a bot token is used. The bot must be invited to the target channel before any message can be posted.
Auth method
Bot OAuth token (xoxb-...) obtained via Slack App OAuth flow. Stored as SLACK_BOT_TOKEN in credential store. Optionally, an incoming webhook URL (SLACK_WEBHOOK_URL) may be used as a simpler alternative for posting-only use cases.
Required scopes
chat:write (post messages to channels the bot has joined); chat:write.public (post to public channels without joining, optional); channels:read (verify target channel exists)
Webhook / trigger setup
Outbound only. The orchestration layer calls POST https://slack.com/api/chat.postMessage with the bot token. No inbound Slack events are consumed in this automation. SLACK_ALERT_CHANNEL_ID stored in credential store (use channel ID, not name, for reliability).
Required configuration
SLACK_BOT_TOKEN and SLACK_ALERT_CHANNEL_ID in credential store. Message template stored as a platform variable: 'Hot lead alert: {prospect_name} ({handle}) replied to the {campaign_name} outreach. HubSpot deal: {deal_url}'. Do not hardcode the channel name or message string.
Rate limits
Slack API Tier 3: 50+ requests/minute for chat.postMessage. At maximum 40 alerts/week (one per prospect reply), volume is negligible. No throttling required.
Constraints
The bot must be a member of SLACK_ALERT_CHANNEL_ID; if it is not invited, the API returns channel_not_found. Slack message formatting uses Block Kit JSON; do not use legacy attachment formatting. If the channel is archived or deleted, the agent must catch the error and log it without retrying indefinitely.
// Post message (Agent 3)
POST https://slack.com/api/chat.postMessage
Authorization: Bearer SLACK_BOT_TOKEN
channel: SLACK_ALERT_CHANNEL_ID
blocks: [
{ type: 'section', text: { type: 'mrkdwn',
text: '*Hot lead:* {prospect_name} replied to {campaign_name}' }},
{ type: 'actions', elements: [
{ type: 'button', text: 'View in HubSpot', url: deal_url }
]}
]Integration and API SpecPage 2 of 4
FS-DOC-05Technical
03Field mappings between tools
The following tables define the exact field mappings for each agent handoff. Source and destination field names are given in monospace as they appear in the API payload or sheet header. All field names are case-sensitive.
Handoff 1: Notion to Prospect Research Agent (campaign brief fields read at trigger)
Source tool
Source field
Destination tool
Destination field
Notion
`properties.Campaign Name.title[0].plain_text`
Orchestration layer (run context)
`campaign_name`
Notion
`properties.Target Niche.rich_text[0].plain_text`
Orchestration layer (run context)
`target_niche`
Notion
`properties.Min Audience Size.number`
Orchestration layer (run context)
`min_audience_size`
Notion
`properties.Max Audience Size.number`
Orchestration layer (run context)
`max_audience_size`
Notion
`properties.Channel.select.name`
Orchestration layer (run context)
`channel`
Notion
`id`
Orchestration layer (run context)
`notion_page_id`
Handoff 2: Prospect Research Agent to Google Sheets (prospect row append)
Source tool
Source field
Destination tool
Destination field
Research agent output
`prospect_name`
Google Sheets
`prospect_name` (col A)
Research agent output
`handle`
Google Sheets
`handle` (col B)
Research agent output
`niche`
Google Sheets
`niche` (col C)
Research agent output
`audience_size_est`
Google Sheets
`audience_size_est` (col D)
Research agent output
`channel`
Google Sheets
`channel` (col E)
Hunter.io
`data.email`
Google Sheets
`email_address` (col F)
Hunter.io
`data.score`
Google Sheets
`hunter_confidence` (col G)
Derived (score >= threshold)
`boolean`
Google Sheets
`email_verified` (col H)
Constant
`'PENDING'`
Google Sheets
`draft_status` (col I)
Handoff 3: Google Sheets to Pitch Drafting Agent (draft generation input)
Source tool
Source field
Destination tool
Destination field
Google Sheets
`prospect_name` (col A)
Gmail (draft body)
`{{prospect_name}}`
Google Sheets
`handle` (col B)
Gmail (draft body)
`{{handle}}`
Google Sheets
`niche` (col C)
Gmail (draft body)
`{{niche}}`
Google Sheets
`email_address` (col F)
Gmail (draft To header)
`To: email_address`
Orchestration context
`campaign_name`
Gmail (draft body)
`{{campaign_name}}`
Orchestration context
`channel`
Gmail (draft body)
`{{channel}}`
Handoff 4: Pitch Drafting Agent to HubSpot (contact record creation)
Source tool
Source field
Destination tool
Destination field
Google Sheets
`email_address`
HubSpot Contacts API
`properties.email`
Google Sheets
`prospect_name` (split on space)
HubSpot Contacts API
`properties.firstname`, `properties.lastname`
Google Sheets
`niche`
HubSpot Contacts API
`properties.niche`
Google Sheets
`channel`
HubSpot Contacts API
`properties.channel`
Orchestration context
`campaign_name`
HubSpot Contacts API
`properties.campaign_tag`
Constant
`'Prospect Identified'`
HubSpot Deals API
`properties.dealstage` (mapped to HUBSPOT_STAGE_ID)
Gmail API response
`draft.id`
Google Sheets
`hubspot_contact_id` (col J) written after HubSpot returns contact ID
Handoff 5: Gmail reply event to Follow-Up and Pipeline Agent (reply detection)
Source tool
Source field
Destination tool
Destination field
Gmail Pub/Sub notification
`message.data` (decoded: `historyId`)
Gmail API
`GET /gmail/v1/users/me/history?startHistoryId={historyId}`
Gmail thread
`messages[].id` where `In-Reply-To` matches sent `Message-ID`
HubSpot Contacts API
trigger deal stage update
Gmail thread
`messages[].snippet`
Slack Block Kit
`text` field in alert message
HubSpot contact lookup
`id` (by email)
HubSpot Deals API
`PATCH /crm/v3/objects/deals/{dealId}` dealstage = 'Replied'
HubSpot deal
`properties.hs_object_id`
Slack alert message
`deal_url` constructed as `https://app.hubspot.com/contacts/{HUBSPOT_PORTAL_ID}/deal/{id}`
Integration and API SpecPage 3 of 4
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
One workflow per agent: Workflow 1 (Prospect Research Agent), Workflow 2 (Pitch Drafting Agent), Workflow 3 (Follow-Up and Pipeline Agent). Workflows share a single credential store scoped to this automation. No agent workflow triggers another directly; handoffs use Google Sheets row status as the shared state layer.
Workflow 1 trigger mechanism
Polling. The orchestration layer polls the Notion campaign briefs database every 5 minutes via authenticated GET request. Trigger condition: Status property equals 'Ready' AND last_edited_time greater than last_poll_timestamp. On match, workflow executes once per matching page and updates last_poll_timestamp.
Workflow 2 trigger mechanism
Polling. The orchestration layer polls the campaign Google Sheet every 10 minutes. Trigger condition: email_verified equals TRUE AND draft_status equals 'PENDING'. Rows matching the condition are processed in batch (up to 20 per run to respect Gmail rate limits). On draft creation, draft_status is updated to 'DRAFTED'.
Workflow 3 trigger mechanism
Webhook (Gmail Pub/Sub) for reply detection. HubSpot webhook for deal stage changes. The orchestration layer exposes two HTTPS endpoints: /webhooks/gmail-reply and /webhooks/hubspot-deal. Gmail Pub/Sub delivers new-mail notifications to /webhooks/gmail-reply; signature validation is performed using the Google Cloud Pub/Sub message attributes. HubSpot delivers deal and contact property changes to /webhooks/hubspot-deal; the X-HubSpot-Signature-v3 header is validated using HMAC-SHA256 with HUBSPOT_WEBHOOK_SECRET before any payload is processed. Requests failing signature validation are rejected with HTTP 401 and logged.
Shared credential store
All secrets are stored in the automation platform's encrypted credential store under the namespace 'influencer-outreach'. No credentials appear in workflow node configuration or code. See code block below for full contents.
Run-state persistence
last_poll_timestamp for Notion and Sheets polling is stored as a platform run-state variable, updated atomically at the end of each successful poll cycle. Gmail watch historyId is stored similarly and updated after each Pub/Sub event is processed.
Execution environment
All three workflows run in the same automation platform project/tenant. Environment: Production. A separate environment (Staging) using sandbox credentials must be maintained for testing. Environment is controlled by the ENVIRONMENT platform variable (values: staging, production).
Full credential store manifest for the influencer-outreach automation namespace
// Credential store contents
// Namespace: influencer-outreach
// All values encrypted at rest; never logged or exposed in error messages
NOTION_API_TOKEN // Notion internal integration token (secret_...)
NOTION_CAMPAIGN_DB_ID // Notion database ID for campaign briefs
GOOGLE_SERVICE_ACCOUNT_JSON // JSON key for Google service account (Sheets + Drive)
GOOGLE_OAUTH_REFRESH_TOKEN // User OAuth refresh token (Gmail)
GOOGLE_CLOUD_PROJECT_ID // GCP project with Gmail API + Pub/Sub enabled
GMAIL_PUBSUB_TOPIC // Full Pub/Sub topic name for Gmail watch
GMAIL_SENDING_ADDRESS // Marketing manager's Gmail address
GMAIL_DRAFT_LABEL_ID // Gmail label ID for 'Outreach-Pending-Review'
GOOGLE_SHEET_TEMPLATE_ID // Google Sheets template file ID
HUNTER_API_KEY // Hunter.io account API key
HUNTER_CONFIDENCE_THRESHOLD // Integer, default: 70
HUBSPOT_PRIVATE_APP_TOKEN // HubSpot private app Bearer token
HUBSPOT_PORTAL_ID // HubSpot account/portal ID
HUBSPOT_PIPELINE_ID // Deal pipeline ID for outreach pipeline
HUBSPOT_SEQUENCE_ID // Sequence ID for follow-up enrolment
HUBSPOT_WEBHOOK_SECRET // HMAC secret for webhook signature validation
// Stage IDs (fetched at build time, stored here):
HUBSPOT_STAGE_PROSPECT // 'Prospect Identified' stage ID
HUBSPOT_STAGE_DRAFTED // 'Pitch Drafted' stage ID
HUBSPOT_STAGE_SENT // 'Pitch Sent' stage ID
HUBSPOT_STAGE_REPLIED // 'Replied' stage ID
HUBSPOT_STAGE_DECLINED // 'Declined' stage ID
SLACK_BOT_TOKEN // Slack bot OAuth token (xoxb-...)
SLACK_ALERT_CHANNEL_ID // Slack channel ID for hot lead alerts
ENVIRONMENT // 'staging' | 'production'
05Error handling and retry logic
Unhandled exceptions must never fail silently. Every error path that is not explicitly listed below must catch the exception, log the full error payload (integration name, endpoint, HTTP status, response body, timestamp, run ID) to the platform's error log, and trigger a Slack alert to SLACK_ALERT_CHANNEL_ID with severity label 'AUTOMATION ERROR'. The marketing team must be able to diagnose any failure from the log alone.
Integration
Scenario
Required behaviour
Notion (polling)
Notion API returns HTTP 429 (rate limit)
Pause the poll cycle. Retry after 60 seconds with exponential backoff (60s, 120s, 240s). After 3 failed retries, log the error and skip the cycle. Do not mark any pages as processed. Alert via Slack.
Notion (polling)
Campaign brief page missing required properties (e.g. Target Niche is empty)
Do not start agent run. Write a comment to the Notion page: 'Automation skipped: required fields missing. Please complete all required brief fields.' Log the page ID and skip. No retry.
Hunter.io (email lookup)
HTTP 404 or status 'email_not_found' returned for a prospect
Write email_verified: FALSE and hunter_confidence: 0 to the Google Sheet row. Set draft_status to 'MANUAL_REVIEW'. Append the prospect name to a 'Needs Manual Review' tab in the sheet. Do not pass the row to the Pitch Drafting Agent. No retry.
Hunter.io (email lookup)
HTTP 429 (monthly quota exceeded)
Halt the Prospect Research Agent run immediately. Do not process remaining rows. Log the error with the quota-exceeded flag. Alert the process owner via Slack: 'Hunter.io quota reached. Remaining prospects require manual email lookup. Upgrade plan or wait for monthly reset.' No automatic retry.
Google Sheets (read/write)
HTTP 429 (quota exceeded during batch operation)
Implement exponential backoff: wait 10s, 20s, 40s before each retry. After 3 retries, log the failing row index and pause the workflow for 60 seconds before resuming. If failure persists after 5 total attempts, log the error, skip the row, and flag it in the sheet with draft_status: 'WRITE_ERROR'.
Google Sheets (read/write)
Sheet column headers do not match expected schema
Abort the agent run immediately. Do not write any rows. Log the schema mismatch with expected vs actual headers. Alert the FullSpec team at support@gofullspec.com and the process owner via Slack. Manual intervention required before the next run.
Gmail (draft creation)
OAuth refresh token expired or revoked
The Pitch Drafting Agent cannot proceed. Log the token error. Send an email alert to GMAIL_SENDING_ADDRESS: 'Gmail authorisation has expired. Please re-authorise the outreach automation at [reauth URL].' Halt the workflow. No retry until re-authorisation is confirmed.
Gmail (Pub/Sub reply detection)
Pub/Sub watch subscription expired (over 7 days since last watch renewal)
The orchestration layer must call users.watch to renew the subscription every 6 days via a scheduled maintenance workflow. If a Pub/Sub notification arrives referencing an expired historyId, fall back to polling GET /gmail/v1/users/me/messages?q=is:inbox after:{last_check_date} for the sending address. Log the fallback event.
HubSpot (contact creation)
Contact already exists (HTTP 409 CONFLICT: duplicate email)
Do not create a duplicate. Call GET /crm/v3/objects/contacts?email={email} to retrieve the existing contact ID. Use the existing ID for all downstream steps. Update the existing contact's campaign_tag property to include the current campaign. Log the deduplication event.
HubSpot (sequence enrolment)
Contact already enrolled in another active HubSpot sequence
Do not attempt to enrol. Log the conflict with the contact ID and existing sequence ID. Write a note to the HubSpot contact timeline: 'Follow-up sequence enrolment skipped: contact is already in an active sequence.' Flag the Google Sheet row with draft_status: 'SEQ_CONFLICT'. Manual review required.
HubSpot (webhook)
Incoming webhook fails signature validation (X-HubSpot-Signature-v3 mismatch)
Reject the request immediately with HTTP 401. Log the raw headers and timestamp. Do not process the payload. Alert the FullSpec team if more than 3 invalid signatures are received within 10 minutes, as this may indicate a replay attack or misconfiguration.
Slack (hot lead alert)
HTTP 404 (channel not found or bot not a member)
Log the error. Attempt to send the alert to a fallback channel (SLACK_FALLBACK_CHANNEL_ID, stored in credential store). If the fallback also fails, log the full error and send an email to GMAIL_SENDING_ADDRESS with the alert content. Do not retry the original channel more than once per run.
Integration and API SpecPage 4 of 4