FS-DOC-05Technical
Integration and API Spec
Outbound Prospecting & Cold Outreach Cadences
[YourCompany.com] · Sales Department · Prepared by FullSpec · [Today's Date]
This document defines every API connection, authentication method, required scope, field mapping, and error behaviour for the Outbound Prospecting and Cold Outreach Cadences automation. It is written for the FullSpec build team and covers the Prospect Enrichment Agent (Agent 1), the Personalisation Drafting Agent (Agent 2), and the Cadence Execution and Logging Agent (Agent 3). All credential handling, rate-limit considerations, and retry logic are specified here so that the orchestration layer can be assembled and tested against a consistent reference.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
HubSpot
CRM record store, sequence enrolment, activity logging, task creation, custom field writes
OAuth 2.0 (private app token)
Sales Hub Professional
Agent 1, Agent 2, Agent 3
Apollo.io
Prospect list pull and contact-level enrichment (job title, company, phone)
API key (Bearer token)
Basic paid tier (API access)
Agent 1
Clearbit
Company and contact enrichment (size, tech stack, recent news, domain lookup)
API key (HTTP Basic, secret key as username)
Growth or above
Agent 1
Gmail
Email delivery for all outreach touches via connected rep account
OAuth 2.0 (Google Identity)
Google Workspace (any tier)
Agent 3
Slack
Rep alert on reply or meeting booking; direct link to HubSpot record
OAuth 2.0 (Bot token via Slack App)
Free or above
Agent 3
Automation platform
Orchestration layer: workflow routing, credential store, retry logic, agent triggering
Internal (platform-managed)
Determined at build
All agents
Before you connect anything: all API credentials must be tested against sandbox or development environments before production keys are loaded into the credential store. For HubSpot, use a developer sandbox account. For Apollo.io and Clearbit, use test-mode keys where available and verify against a known sample contact before enabling live enrichment. Gmail connections must be authorised under the rep's own Google account, not a shared mailbox, to satisfy OAuth identity requirements and HubSpot's connected-inbox rules.
02Per-tool integration detail
HubSpot
Central CRM used by all three agents. Agent 1 writes enrichment data to contact properties. Agent 2 writes the personalisation draft and creates a review task. Agent 3 manages sequence enrolment, reads task completion status, logs all touch events, and updates deal/contact lifecycle stage.
Auth method
OAuth 2.0 via HubSpot Private App. Generate a private app token in Settings > Integrations > Private Apps. Store the token in the credential store as HUBSPOT_PRIVATE_APP_TOKEN. 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, crm.schemas.contacts.write, sales-email-read, crm.objects.owners.read, timeline, tasks, automation, conversations.read
Webhook / trigger setup
Subscribe to the contact.propertyChange webhook event filtered on the hs_lead_status property. Target value: 'To Contact'. The automation platform registers the webhook endpoint via the HubSpot Webhooks API (POST /webhooks/v3/{appId}/subscriptions). Validate inbound payloads using the X-HubSpot-Signature-v3 header with the app client secret (HMAC-SHA256). Task completion (rep approval) is polled every 90 seconds against the engagements v2 API filtering task status = COMPLETED and task subject containing 'Review personalisation draft'.
Required configuration
Custom contact properties must be created before build: personalisation_draft (single-line text), enrichment_status (dropdown: Pending / Complete / Failed), clearbit_company_size (number), clearbit_tech_stack (multi-line text), apollo_job_title (single-line text), trigger_event_note (multi-line text). HubSpot sequence IDs to be enrolled must be stored as HUBSPOT_SEQUENCE_ID_DEFAULT (and per-persona variants) in the credential store, never hardcoded. Sequence templates must include the {{personalisation_draft}} token as the opening line of touch one.
Rate limits
Private app tokens: 110 requests/10 seconds and 250,000 requests/day. At 120 prospects/month (~4/day), peak burst is well under threshold. No throttling wrapper is required at current volume, but the orchestration layer must honour 429 responses with exponential backoff starting at 1 second.
Constraints
Sequences require Sales Hub Professional or Enterprise seat. Sequence enrolment via API requires the contact to not already be enrolled in another active sequence. The connected Gmail inbox used for sending must match the HubSpot user whose sequence is being enrolled; cross-user enrolment is not supported via the sequences API.
Apollo.io
Used by Agent 1 (Prospect Enrichment Agent) to retrieve contact-level data including verified job title, direct phone, LinkedIn URL, company employee count, and industry vertical for each new prospect entering the pipeline.
Auth method
API key passed as a Bearer token in the Authorization header. Store as APOLLO_API_KEY in the credential store.
Required scopes
Apollo.io uses key-based auth without discrete OAuth scopes. The API key must belong to an account with People Search and Contact Export permissions enabled. Confirm under Apollo Settings > API > Permissions that 'Export contacts' is checked.
Webhook / trigger setup
No inbound webhook from Apollo.io. Agent 1 calls Apollo outbound on demand: POST /v1/people/match with email and/or first_name, last_name, organization_name as lookup parameters.
Required configuration
Store base URL as APOLLO_BASE_URL = https://api.apollo.io/v1. Field mapping from Apollo response to HubSpot properties must be defined in the orchestration layer config (see Section 03). The reveal_personal_emails parameter should be set to false unless specifically required, to respect Apollo data-use terms.
Rate limits
Basic paid tier: 600 requests/minute. At 120 prospects/month the daily call volume is approximately 8 enrichment calls/day, well within limits. No throttling required at current volume. Implement a 60-second retry on 429 responses.
Constraints
Apollo match confidence score is returned as a float (0 to 1). If the match score is below 0.75, the orchestration layer must flag the record with enrichment_status = 'Needs Review' and skip Apollo data writes to prevent low-confidence overwrites of known fields. Apollo data must not be stored beyond the enrichment workflow use case per their ToS.
Clearbit
Used by Agent 1 alongside Apollo.io to retrieve company-level intelligence: employee count, industry, tech stack (technologies_used), and recent news or funding events that can serve as personalisation hooks.
Auth method
HTTP Basic Auth. Clearbit secret key is passed as the username; the password field is left blank. Store as CLEARBIT_SECRET_KEY in the credential store. Base URL: https://person.clearbit.com for person enrichment; https://company.clearbit.com for company enrichment.
Required scopes
Clearbit does not use OAuth scopes. The API key must be provisioned on a plan with Enrichment API access (Growth or above). Combined enrichment endpoint (GET /v2/combined/find) requires both Person and Company enrichment to be active on the account.
Webhook / trigger setup
Clearbit supports streaming enrichment via webhook for async results. Configure the webhook notify URL on the Clearbit dashboard to point to the orchestration platform's inbound webhook endpoint. Store the webhook signing secret as CLEARBIT_WEBHOOK_SECRET and validate all inbound payloads by comparing the X-Request-Signature header (HMAC-SHA256 of the raw body) before processing.
Required configuration
Use the combined enrichment endpoint GET /v2/combined/find?email={email} as the primary call. If the email is unavailable, fall back to GET /v1/companies/find?domain={company_domain}. Store the company domain lookup as a secondary enrichment path. Map tech_stack array to clearbit_tech_stack (pipe-delimited string) and employment.seniority + employment.title to apollo_job_title as a fallback when Apollo returns low confidence.
Rate limits
Growth plan: 1,200 enrichment calls/month included; overage billed per call. At 120 prospects/month plus re-enrichment retries, expected usage is approximately 150 to 180 calls/month. No throttling wrapper needed but the orchestration layer must count monthly usage and alert via Slack to the build channel if usage exceeds 1,000 calls in a calendar month.
Constraints
Clearbit may return a 202 Accepted (async) instead of a 200 with data when the record is not yet cached. The orchestration layer must handle this by waiting for the webhook callback (max wait: 30 seconds) before proceeding. If no callback arrives within 30 seconds, mark enrichment_status = 'Partial' and continue with Apollo data only.
Integration and API SpecPage 1 of 3
FS-DOC-05Technical
Gmail
Used by Agent 3 (Cadence Execution and Logging Agent) as the sending inbox for all outreach touches. Email delivery is executed through HubSpot's connected inbox feature, which routes sends via the authenticated Gmail account. Direct Gmail API calls are used only for read operations (open/click tracking is handled by HubSpot).
Auth method
OAuth 2.0 via Google Identity Platform. The rep's Gmail account must be connected to HubSpot under Settings > General > Email Integrations using the IMAP/SMTP connected inbox method. Store the Google OAuth refresh token as GMAIL_OAUTH_REFRESH_TOKEN and the client credentials as GMAIL_CLIENT_ID and GMAIL_CLIENT_SECRET in the credential store.
Required scopes
https://www.googleapis.com/auth/gmail.send, https://www.googleapis.com/auth/gmail.readonly, https://www.googleapis.com/auth/gmail.modify, https://mail.google.com/
Webhook / trigger setup
Gmail push notifications (Google Cloud Pub/Sub) are used to detect inbound replies. Configure a Pub/Sub topic and subscription, then call POST /gmail/v1/users/me/watch with the topic name and labelIds: ['INBOX']. Store the Pub/Sub subscription endpoint as GMAIL_PUBSUB_SUBSCRIPTION. Validate Pub/Sub push messages using the Google-signed JWT in the Authorization header before processing any reply detection logic.
Required configuration
The Gmail account used for sending must be the same account connected to HubSpot Sequences. Daily sending volume must not exceed Google Workspace limits (2,000 emails/day for standard Workspace accounts). At 120 prospects/month across a 6 to 8 touch cadence, peak volume is approximately 30 to 40 emails/day per rep, well within limits. Store the rep's Gmail address as GMAIL_SENDER_ADDRESS in the credential store.
Rate limits
Gmail API: 250 quota units/user/second; send operations cost 100 units each. At current volume, peak is approximately 3 sends/minute, equating to 300 units/minute, within the per-minute quota. No throttling required at current volume. Implement a 5-second delay between programmatic send calls as a precautionary measure.
Constraints
Gmail OAuth tokens expire after 1 hour. The orchestration layer must use the refresh token to obtain a new access token automatically before any send operation. Token refresh failures must trigger an immediate Slack alert to the build channel and halt send operations until re-authorisation is confirmed by the rep.
Slack
Used by Agent 3 to deliver real-time alerts to the rep's Slack channel when a prospect replies to a sequence touch or clicks a meeting booking link. Each alert includes the contact name, the touch number that triggered the reply, and a deep link to the HubSpot contact record.
Auth method
OAuth 2.0 via Slack App installation. Create a Slack App in the Slack API console, install it to the workspace, and store the Bot User OAuth Token as SLACK_BOT_TOKEN. The bot must be invited to the alert channel before messages can be posted.
Required scopes
chat:write, chat:write.public, channels:read, users:read, users:read.email
Webhook / trigger setup
Outbound only. The orchestration layer calls POST https://slack.com/api/chat.postMessage with the SLACK_BOT_TOKEN. The target channel ID is stored as SLACK_ALERT_CHANNEL_ID in the credential store. No inbound webhook from Slack is required for this process.
Required configuration
The Slack message payload must use Block Kit format with a Section block containing the prospect's full name, company, and the triggering event (reply or booking), an Action block with a button linking to the HubSpot record URL (constructed as https://app.hubspot.com/contacts/{portal_id}/contact/{contact_id}), and a Context block showing the touch number and timestamp. Store the HubSpot portal ID as HUBSPOT_PORTAL_ID in the credential store for link construction.
Rate limits
Slack Web API: 1 request/second per method (Tier 3 for chat.postMessage). At current volume (~4 alerts/day), no throttling is required. The orchestration layer must queue Slack messages and respect a 1-second minimum interval between consecutive posts if batch alerts are triggered.
Constraints
The Slack bot must remain installed and the SLACK_BOT_TOKEN must not be rotated without updating the credential store. If the bot is removed from the workspace or the channel, the orchestration layer must catch the channel_not_found or not_in_channel error and fall back to logging the alert to HubSpot as an internal note on the contact record.
03Field mappings between tools
The following tables document the exact field-level mappings for each agent handoff. Field names shown in the Source field and Destination field columns use their exact API property names as they appear in each tool's schema.
Agent 1 handoff: Apollo.io and Clearbit to HubSpot (Prospect Enrichment Agent output)
Source tool
Source field
Destination tool
Destination field
Apollo.io
person.title
HubSpot
jobtitle
Apollo.io
person.linkedin_url
HubSpot
hs_linkedin_url
Apollo.io
person.phone_numbers[0].raw_number
HubSpot
phone
Apollo.io
organization.name
HubSpot
company
Apollo.io
organization.estimated_num_employees
HubSpot
clearbit_company_size
Apollo.io
organization.industry
HubSpot
industry
Clearbit
person.employment.seniority
HubSpot
apollo_job_title
Clearbit
company.tech[*].tag
HubSpot
clearbit_tech_stack
Clearbit
company.metrics.employees
HubSpot
clearbit_company_size
Clearbit
company.site.domain
HubSpot
website
Clearbit
company.category.industry
HubSpot
industry
Clearbit (async)
person.employment.title
HubSpot
jobtitle
Orchestration layer
enrichment result status string
HubSpot
enrichment_status
Orchestration layer
trigger_event string (constructed)
HubSpot
trigger_event_note
Agent 2 handoff: HubSpot enriched fields to AI writing step, then back to HubSpot (Personalisation Drafting Agent)
Source tool
Source field
Destination tool
Destination field
HubSpot
jobtitle
AI prompt context
{{contact_job_title}}
HubSpot
company
AI prompt context
{{company_name}}
HubSpot
clearbit_tech_stack
AI prompt context
{{tech_stack}}
HubSpot
trigger_event_note
AI prompt context
{{trigger_event}}
HubSpot
clearbit_company_size
AI prompt context
{{company_size}}
AI writing step
generated_opening_line (string)
HubSpot
personalisation_draft
Orchestration layer
task object (subject, body, due date)
HubSpot
hs_task_subject / hs_task_body / hs_timestamp
Agent 3 handoff: HubSpot to Gmail (send), HubSpot to Slack (alert), and event data back to HubSpot (Cadence Execution and Logging Agent)
Source tool
Source field
Destination tool
Destination field
HubSpot
personalisation_draft
Gmail / HubSpot sequence
{{personalisation_draft}} token in touch one template
HubSpot
hs_email_sequence_id (stored as HUBSPOT_SEQUENCE_ID_DEFAULT)
HubSpot Sequences API
sequenceId parameter in enrolment call
HubSpot
hs_object_id (contact)
HubSpot Sequences API
contactId parameter in enrolment call
HubSpot
hubspot_owner_id
HubSpot Sequences API
userId parameter (sequence sender must match owner)
Gmail Pub/Sub
message.payload.headers[From]
HubSpot
hs_email_from_email (engagement log)
Gmail Pub/Sub
message.internalDate
HubSpot
hs_timestamp (engagement log)
HubSpot engagement event
hs_email_status (SENT/OPENED/CLICKED/REPLIED)
HubSpot
lifecyclestage, hs_lead_status
HubSpot contact
hs_object_id, firstname, lastname, company
Slack Block Kit payload
contact name and company in Section block
HubSpot contact
hs_object_id
Slack Block Kit payload
button URL constructed with HUBSPOT_PORTAL_ID
HubSpot engagement
engagement.type (EMAIL), engagement.metadata.status
Slack Block Kit payload
trigger event label in Context block
Integration and API SpecPage 2 of 3
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Three discrete workflows, one per agent. Workflow 1: Prospect Enrichment Agent. Workflow 2: Personalisation Drafting Agent. Workflow 3: Cadence Execution and Logging Agent. Each workflow is self-contained with its own error handling and retry logic. Shared state between workflows is passed exclusively via HubSpot contact properties, not via in-memory variables or local files. All credentials are read from a centralised credential store at runtime; no credential is hardcoded in any workflow definition.
Agent 1 trigger mechanism
Webhook (inbound). HubSpot fires a contact.propertyChange event when hs_lead_status is set to 'To Contact'. The automation platform registers a webhook subscription endpoint and listens for this event. Payload signature validated using X-HubSpot-Signature-v3 (HMAC-SHA256) before processing begins.
Agent 2 trigger mechanism
Webhook (inbound, chained). Agent 1 writes enrichment_status = 'Complete' to the HubSpot contact on completion. A second HubSpot webhook subscription on contact.propertyChange filtered on enrichment_status = 'Complete' fires Agent 2. Signature validation applies identically to Agent 1.
Agent 3 trigger mechanism
Poll (task completion). The orchestration layer polls the HubSpot Engagements API every 90 seconds for tasks with status = COMPLETED and subject containing 'Review personalisation draft'. On match, Agent 3 is triggered with the associated contactId. Gmail reply detection uses Google Cloud Pub/Sub push subscription (webhook, JWT-validated). Both trigger paths feed into the same Agent 3 workflow via conditional branching on the trigger_source context variable.
Credential store structure
All secrets are stored in the automation platform's encrypted credential store. See code block below for the full list of required credential entries.
All entries must be populated before any workflow is activated. Never commit these values to version control.
# Credential store entries (all required before first workflow run)
# HubSpot
HUBSPOT_PRIVATE_APP_TOKEN = <private app token from HubSpot developer settings>
HUBSPOT_PORTAL_ID = <numeric portal ID, e.g. 12345678>
HUBSPOT_WEBHOOK_APP_CLIENT_SECRET= <client secret for X-HubSpot-Signature-v3 validation>
HUBSPOT_SEQUENCE_ID_DEFAULT = <sequence ID for primary outreach cadence>
HUBSPOT_SEQUENCE_ID_PERSONA_B = <optional: alternate sequence ID for secondary persona>
# Apollo.io
APOLLO_API_KEY = <api key from Apollo Settings > API>
APOLLO_BASE_URL = https://api.apollo.io/v1
# Clearbit
CLEARBIT_SECRET_KEY = <secret key from Clearbit dashboard>
CLEARBIT_WEBHOOK_SECRET = <signing secret for X-Request-Signature validation>
# Gmail / Google
GMAIL_CLIENT_ID = <OAuth 2.0 client ID from Google Cloud Console>
GMAIL_CLIENT_SECRET = <OAuth 2.0 client secret>
GMAIL_OAUTH_REFRESH_TOKEN = <long-lived refresh token for rep account>
GMAIL_SENDER_ADDRESS = <rep Gmail address, e.g. rep@yourcompany.com>
GMAIL_PUBSUB_SUBSCRIPTION = <Pub/Sub subscription resource name>
# Slack
SLACK_BOT_TOKEN = <Bot User OAuth Token, prefix xoxb->
SLACK_ALERT_CHANNEL_ID = <channel ID, e.g. C0123456789>
# AI writing step
AI_MODEL_API_KEY = <API key for the configured LLM provider>
AI_PROMPT_TEMPLATE_ID = <reference ID or version hash of the approved prompt template>
# Orchestration platform internal
WORKFLOW_ENV = production # or: staging
CLEARBIT_MONTHLY_CALL_COUNTER = 0 # reset to 0 on first day of each calendar month
The credential store must be the single source of truth for all secrets. If a credential is rotated (for example, HubSpot token regenerated, Gmail OAuth re-authorised, Slack app reinstalled), update the credential store entry and restart the affected workflow. Stale credentials are the most common cause of silent integration failures in this stack.
05Error handling and retry logic
Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently: if a scenario is not covered by a retry or fallback rule below, the orchestration layer must log the full error payload to a persistent error log, set the affected HubSpot contact's enrichment_status or a dedicated automation_error flag to indicate the failure state, and post an alert to the Slack build channel (distinct from the rep alert channel).
Integration
Scenario
Required behaviour
HubSpot (all agents)
429 Too Many Requests
Retry with exponential backoff: wait 1s, 2s, 4s, 8s (max 4 attempts). If all retries fail, log the failed operation to the error log and post alert to Slack build channel. Do not drop the record.
HubSpot (all agents)
401 Unauthorized (token expired or revoked)
Immediately halt all three workflows. Post a critical alert to the Slack build channel with the instruction to regenerate the private app token and update HUBSPOT_PRIVATE_APP_TOKEN. Do not retry automatically; this requires human action.
HubSpot (Agent 2)
Custom contact property missing (e.g. personalisation_draft not found)
Catch 404 on property schema call. Log the missing property name, halt Agent 2, and post alert to build channel. Property must be created manually before Agent 2 can resume.
Apollo.io (Agent 1)
No match returned (404 or empty results)
Set enrichment_status = 'Partial'. Write any available fields. Continue to Clearbit enrichment. Do not halt the workflow. Log the unmatched record with the lookup parameters used.
Apollo.io (Agent 1)
Match confidence score below 0.75
Discard Apollo field values for this record. Set enrichment_status = 'Needs Review'. Proceed to Clearbit. Flag the contact in HubSpot with a note: 'Apollo low-confidence match, manual verification recommended'.
Apollo.io (Agent 1)
429 or 503 rate limit or service error
Retry with exponential backoff: 60s, 120s, 240s (max 3 attempts). If still failing, proceed with Clearbit-only enrichment and log Apollo as unavailable for this record.
Clearbit (Agent 1)
202 Accepted (async, no immediate data)
Wait for webhook callback up to 30 seconds. If callback not received, mark enrichment_status = 'Partial' and proceed with Apollo data only. Log the contact ID and timestamp for deferred retry.
Clearbit (Agent 1)
404 Not Found (company or person not in Clearbit database)
Accept partial enrichment. Set enrichment_status = 'Partial'. Continue workflow with whatever fields are populated. Do not block Agent 2 from running on partial data.
Gmail OAuth (Agent 3)
Access token expired (401 on send attempt)
Automatically use GMAIL_OAUTH_REFRESH_TOKEN to request a new access token via the Google token endpoint. Retry the send operation once with the new token. If refresh fails (invalid_grant), halt sends and post a critical alert to the Slack build channel; manual re-authorisation is required.
Gmail send (Agent 3)
Daily sending limit reached (403 quotaExceeded)
Halt all further sends for the calendar day. Queue pending sends for the next day at 08:00 local time. Post alert to rep's Slack channel and build channel noting the queue pause and resume time.
Slack (Agent 3)
channel_not_found or not_in_channel error on chat.postMessage
Fall back to creating an internal HubSpot note on the contact record with the alert content. Log the Slack failure to the error log. Post a recovery alert to the build channel to prompt channel membership check.
HubSpot Sequences API (Agent 3)
Contact already enrolled in an active sequence (409 Conflict)
Do not re-enrol. Log the conflict to HubSpot as a contact note: 'Enrolment skipped, contact already in active sequence'. Set a HubSpot task for rep review within 24 hours.
Any integration
Unhandled exception (5xx, timeout > 30s, or unexpected response schema)
Log the full error payload (status code, response body, workflow step, contact ID, timestamp) to the persistent error log. Set automation_error = true on the HubSpot contact. Post alert to Slack build channel. Never proceed to the next workflow step on an unhandled exception.
For any failure that sets enrichment_status = 'Partial' or automation_error = true on a HubSpot contact, the FullSpec team will review the error log weekly during the pilot phase. A HubSpot saved view filtered on automation_error = true must be created before go-live so that the rep team can surface and manually resolve affected records without needing access to the orchestration platform logs. Contact support@gofullspec.com if a failure pattern is recurring or unclear.
Integration and API SpecPage 3 of 3