FS-DOC-05Technical
Integration and API Spec
Customer Segmentation & List Management
[YourCompany.com] · Marketing Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for every API connection, credential, field mapping, and failure behaviour in the Customer Segmentation and List Management automation. It is written for the FullSpec build team and covers HubSpot, Typeform, Segment, Mailchimp, Google Sheets, and Slack. Each section specifies exact scope strings, rate limits, webhook validation requirements, and retry behaviour so that the orchestration layer can be configured without ambiguity. The process owner and marketing team do not need to read this document; all owner-facing guidance is in the accompanying Runbook and Launch Plan.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Orchestration layer
Hosts all three agent workflows, credential store, retry logic, and inter-agent handoff
Internal service account
N/A (FullSpec-managed)
All agents
HubSpot
Source of truth for contact records; receives segment tags and suppression flags
Private App token (OAuth 2.0 not required for server-side)
Marketing Hub Starter or above
Agent 1, Agent 2
Typeform
Inbound lead capture; pushes new responses via webhook to Agent 1
Webhook HMAC-SHA256 signature + API key for backfill pulls
Basic plan or above
Agent 1
Segment
Deduplication and data routing between agents
Write Key (source) + API token (Profiles API)
Free tier (Connections); Profiles feature requires Team plan
Agent 1, Agent 2
Mailchimp
Email audience destination; suppression list source
API key (server-side OAuth not required)
Essentials plan or above (API access)
Agent 2
Google Sheets
Append-only run log and exception tracker
OAuth 2.0 service account (JSON key)
Free (Google Workspace or personal)
Agent 3
Slack
Run summary notification destination
Bot token (OAuth 2.0, xoxb- prefix)
Free or above
Agent 3
Before you connect anything: create sandbox or test accounts for every tool below and validate all credentials and webhook endpoints in those environments before promoting to production. Never paste production API keys into a test workflow and never write live contact records during integration testing.
Integration and API SpecPage 1 of 4
FS-DOC-05Technical
02Per-tool integration detail
HubSpot
Used by Agent 1 (Data Merge and Dedup) to pull contact records on trigger, and by Agent 2 (Segmentation Rules) to write segment tags and suppression flags back to each contact. All calls use the HubSpot Contacts v3 API.
Auth method
Private App token. Generate under Settings > Integrations > Private Apps. Store as HUBSPOT_PRIVATE_APP_TOKEN in the credential store. No OAuth 2.0 flow is required for server-to-server use.
Required scopes
crm.objects.contacts.read, crm.objects.contacts.write, crm.schemas.contacts.read, crm.schemas.contacts.write, crm.lists.read, crm.lists.write, crm.objects.companies.read
Webhook / trigger setup
Configure a CRM webhook subscription at Settings > Integrations > Webhooks. Subscribe to the contact.creation and contact.propertyChange events. Set the target URL to the orchestration layer's inbound webhook endpoint. HubSpot signs payloads with X-HubSpot-Signature-v3 (HMAC-SHA256 of the client secret + HTTP method + URI + request body). Validate this signature on every inbound request before processing.
Required configuration
Custom contact properties required: hs_segment_tag (enumeration, values defined in segment rules doc), hs_suppression_flag (boolean), hs_dedup_merged_from (string, comma-separated VIDs of merged records), hs_last_segment_run (datetime). Store the HubSpot portal ID as HUBSPOT_PORTAL_ID in the credential store, not hardcoded.
Rate limits
10 requests/second (rolling) on Starter plan; burst to 100/10 seconds. At ~400 contact updates/month the sustained call rate is well under 1 request/second. No throttling wrapper is required. Implement exponential backoff on 429 responses only as a precaution.
Constraints
Batch read endpoint (POST /crm/v3/objects/contacts/batch/read) accepts max 100 IDs per call. Batch write (POST /crm/v3/objects/contacts/batch/update) also 100 per call. Paginate with after cursor from the search API. Do not use the legacy v1 Contacts API.
// Inbound webhook payload (contact.propertyChange)
{
"objectId": 12345678,
"subscriptionType": "contact.propertyChange",
"propertyName": "email",
"propertyValue": "user@example.com",
"occurredAt": 1718500000000,
"portalId": 99999999
}
// Agent 1 read call
GET /crm/v3/objects/contacts/{objectId}
?properties=email,firstname,lastname,phone,lifecyclestage,
hs_lead_status,hs_segment_tag,hs_suppression_flag,
hs_last_modified_date,associatedcompanyid
// Agent 2 write call (batch update)
POST /crm/v3/objects/contacts/batch/update
body: { "inputs": [
{ "id": "12345678",
"properties": {
"hs_segment_tag": "high_value_buyer",
"hs_suppression_flag": "false",
"hs_last_segment_run": "2024-06-16T10:00:00Z"
}
}
]}Typeform
Delivers new lead capture responses to Agent 1 via a real-time webhook. A scheduled backfill poll covers any webhook delivery failures. Used by Agent 1 only.
Auth method
Webhook secret (HMAC-SHA256) for inbound payload validation. Typeform Personal Access Token (store as TYPEFORM_API_TOKEN) for backfill polling via the Responses API.
Required scopes
responses:read (Responses API), webhooks:write (to register and update the webhook endpoint via API if needed). These are Typeform API token scopes set at token creation.
Webhook / trigger setup
Register the webhook in the Typeform admin under Connect > Webhooks for the target form. Set the payload URL to the orchestration inbound endpoint. Enable the secret field and store the value as TYPEFORM_WEBHOOK_SECRET. The orchestration layer must validate the Typeform-Signature header (sha256=HMAC of raw body using the secret) before accepting the payload.
Required configuration
Store TYPEFORM_FORM_ID (the specific form ID for lead capture) in the credential store. Map Typeform field IDs to canonical contact fields in the field mapping table (section 03). Do not hardcode field IDs; they must be read from the credential/config store so they can be updated without a code change.
Rate limits
Responses API: 10 requests/second. Backfill poll runs at most once per hour; this equates to well under 1 request/minute at current volume. No throttling needed.
Constraints
Responses API paginates with page_size (max 1000) and before/after token cursors. The backfill job must store the last_processed_token between runs to avoid reprocessing. Typeform webhook retries on non-2xx response with up to 3 attempts over 24 hours; the orchestration endpoint must return 200 immediately and process asynchronously.
// Inbound webhook payload (abbreviated)
{
"event_id": "abc123",
"event_type": "form_response",
"form_response": {
"form_id": "FORM_ID_FROM_CONFIG",
"submitted_at": "2024-06-16T09:45:00Z",
"answers": [
{ "field": { "id": "field_001", "type": "email" }, "email": "user@example.com" },
{ "field": { "id": "field_002", "type": "short_text" }, "text": "Jane Smith" },
{ "field": { "id": "field_003", "type": "phone_number" }, "phone_number": "+15550001234" }
]
}
}Segment (Twilio Segment)
Acts as the deduplication and data routing layer between Agent 1 and Agent 2. Agent 1 sends identify and track calls to Segment; Agent 2 queries the Profiles API to retrieve the resolved canonical identity before applying segment rules.
Auth method
Write Key (stored as SEGMENT_WRITE_KEY) for identify/track calls from Agent 1. Segment Public API token (stored as SEGMENT_API_TOKEN) for Profiles API reads and Engage audience management by Agent 2. Both stored in the credential store.
Required scopes
sources:write (for event ingestion), profiles:read (Profiles API, requires Unify feature on Team plan), audiences:write (Engage, if used for audience sync), destinations:read (to verify connection status).
Webhook / trigger setup
No inbound webhook. Segment is called synchronously by the orchestration layer. If Segment Engage is used for downstream Mailchimp sync, configure a Mailchimp destination in the Segment workspace with the audience IDs mapped (stored as SEGMENT_MAILCHIMP_DESTINATION_ID). Validate the destination connection in the Segment dashboard before enabling live writes.
Required configuration
SEGMENT_WRITE_KEY, SEGMENT_API_TOKEN, SEGMENT_SPACE_ID (Profiles/Unify space ID), SEGMENT_MAILCHIMP_DESTINATION_ID all stored in credential store. The deduplication merge strategy must be set to email as the identity graph merge key in the Segment Unify settings. Do not use phone as a primary merge key without explicit confirmation of data quality.
Rate limits
Track/identify calls: no documented hard limit on Team plan; practically governed by ingest throughput. At 400 events/month the load is negligible. Profiles API: 100 requests/second. No throttling required at current volume.
Constraints
Segment Profiles (Unify) is only available on Team plan or above. Verify plan tier before build. Identity resolution is eventually consistent; allow up to 60 seconds after an identify call before querying the Profiles API for a merged record in integration tests. Segment does not delete source events; merged duplicate profiles are resolved at query time only.
// Agent 1 -> Segment identify call
POST https://api.segment.io/v1/identify
Authorization: Basic base64(SEGMENT_WRITE_KEY:)
{
"userId": "hs_12345678",
"traits": {
"email": "user@example.com",
"firstName": "Jane",
"lastName": "Smith",
"phone": "+15550001234",
"source": "hubspot"
}
}
// Agent 2 -> Segment Profiles API read
GET https://profiles.segment.com/v1/spaces/{SEGMENT_SPACE_ID}/collections/users/profiles/email:user@example.com
Authorization: Basic base64(SEGMENT_API_TOKEN:)
// Returns canonical profile with merged userId listMailchimp
Receives segment audience updates from Agent 2 and provides the suppression list (unsubscribes and bounces) for Agent 2 to exclude before syncing. Used by Agent 2 only.
Auth method
API key authentication. Store as MAILCHIMP_API_KEY in the credential store. The data centre suffix (e.g. us6) must also be stored as MAILCHIMP_DC and used to construct the base URL (https://{MAILCHIMP_DC}.api.mailchimp.com/3.0/).
Required scopes
Mailchimp API keys grant full account access; scope restriction is not available at the key level. Mitigate this by creating a dedicated Mailchimp account user with Member role restricted to the relevant audience lists, then generating the API key from that account.
Webhook / trigger setup
No inbound webhook from Mailchimp is required. Agent 2 polls the suppression list on each run using GET /3.0/lists/{list_id}/members?status=unsubscribed and GET /3.0/lists/{list_id}/members?status=cleaned (bounced). Audience IDs for each segment must be stored in the credential store as MAILCHIMP_AUDIENCE_{SEGMENT_NAME}_ID, not hardcoded.
Required configuration
MAILCHIMP_API_KEY, MAILCHIMP_DC, MAILCHIMP_DEFAULT_LIST_ID (master audience), and per-segment audience IDs (MAILCHIMP_AUDIENCE_HIGH_VALUE_ID, MAILCHIMP_AUDIENCE_LAPSED_ID, MAILCHIMP_AUDIENCE_NEWSLETTER_ONLY_ID, and any additional segments). Merge fields must be confirmed against the Mailchimp audience's merge field schema before writing; required merge fields that are blank will reject the member update.
Rate limits
10 simultaneous connections per API key; 10 requests/second. Batch operations endpoint (POST /3.0/batches) should be used when syncing more than 50 members per run to stay within per-second limits. At 400 updates/month, sustained throughput is under 0.02 requests/second. Use batch endpoint as standard practice, not just at volume thresholds.
Constraints
Mailchimp does not allow re-subscribing a contact with status=unsubscribed via API; any such attempt returns a 400 error. The orchestration layer must catch this and log the contact as permanently suppressed without retrying. Archived contacts cannot be updated; check status before any PATCH call.
// Agent 2 -> suppression list pull
GET /3.0/lists/{MAILCHIMP_DEFAULT_LIST_ID}/members
?status=unsubscribed&count=1000&offset=0
// Paginate using total_items vs offset
// Agent 2 -> batch segment sync
POST /3.0/batches
{
"operations": [
{
"method": "PUT",
"path": "/lists/{audience_id}/members/{md5_email}",
"body": "{\"email_address\":\"user@example.com\",\"status_if_new\":\"subscribed\",\"merge_fields\":{\"FNAME\":\"Jane\",\"LNAME\":\"Smith\"},\"tags\":[\"high_value_buyer\"]}"
}
]
}Google Sheets
Receives append-only log rows from Agent 3 after each automation run. Stores segment counts, record change totals, flagged contacts, and run timestamps for audit purposes.
Auth method
OAuth 2.0 service account. Create a service account in Google Cloud Console, download the JSON key, and store as GOOGLE_SERVICE_ACCOUNT_JSON in the credential store. Share the target Google Sheet with the service account's email address (Editor role).
Required scopes
https://www.googleapis.com/auth/spreadsheets (read/write to Sheets), https://www.googleapis.com/auth/drive.file (required to access files shared with the service account).
Webhook / trigger setup
No webhook. Agent 3 calls the Sheets API synchronously after each run. Store the target spreadsheet ID as GSHEETS_LOG_SPREADSHEET_ID and the log tab name as GSHEETS_LOG_SHEET_NAME in the credential store.
Required configuration
The log sheet must have the following column headers in row 1: run_timestamp, agent_name, records_processed, records_changed, segments_updated, flagged_for_review, run_status, notes. Agent 3 appends one row per run using the spreadsheets.values.append method with valueInputOption=USER_ENTERED. Do not write to row 1 (headers).
Rate limits
500 requests per 100 seconds per project; 100 requests per 100 seconds per user. Agent 3 makes 1 append call per run. At current volume this is negligible. No throttling required.
Constraints
The Sheets API append operation is not atomic; concurrent writes from parallel runs could interleave rows. Ensure only one instance of Agent 3 runs at a time (enforce at the orchestration layer with a run lock or sequential trigger).
// Agent 3 -> append log row
POST https://sheets.googleapis.com/v4/spreadsheets/{GSHEETS_LOG_SPREADSHEET_ID}
/values/{GSHEETS_LOG_SHEET_NAME}!A:H:append
?valueInputOption=USER_ENTERED
{
"values": [[
"2024-06-16T10:05:00Z",
"Reporting and Notification Agent",
"412",
"38",
"4",
"2",
"success",
"2 contacts flagged: ambiguous segment; routed to marketing manager review"
]]
}Slack
Receives a structured run summary message from Agent 3 after each automation run. The message is posted to a designated marketing ops channel. Used by Agent 3 only.
Auth method
Slack Bot Token (xoxb- prefix). Create a Slack App in the target workspace, add the bot to the workspace, and store the bot token as SLACK_BOT_TOKEN in the credential store. Do not use legacy webhook URLs.
Required scopes
chat:write (post messages to channels the bot is a member of), channels:join (to programmatically join a channel if needed), users:read.email (optional, only if mapping Slack users to flagged contacts for direct DM fallback).
Webhook / trigger setup
No inbound webhook. Agent 3 calls POST https://slack.com/api/chat.postMessage with the channel ID stored as SLACK_CHANNEL_ID in the credential store. Use the channel ID (e.g. C0XXXXXXXX), not the channel name, to prevent breakage on channel renames.
Required configuration
SLACK_BOT_TOKEN, SLACK_CHANNEL_ID stored in the credential store. The bot must be invited to the channel manually (/invite @BotName) before the first run. The message payload uses Block Kit with a header block, a fields block for segment counts, and a context block for flagged contact count.
Rate limits
Tier 3 method (chat.postMessage): 1 request/second. Agent 3 sends 1 message per run. No throttling required.
Constraints
Slack message text must not exceed 4000 characters per block. If the segment count summary would exceed this (unlikely at current volume), truncate to the top 10 segments by record count and append a link to the Google Sheets log. Block Kit attachments are deprecated; use blocks only.
// Agent 3 -> Slack post
POST https://slack.com/api/chat.postMessage
Authorization: Bearer {SLACK_BOT_TOKEN}
{
"channel": "{SLACK_CHANNEL_ID}",
"blocks": [
{ "type": "header", "text": { "type": "plain_text", "text": "Segmentation Run Complete" } },
{ "type": "section", "fields": [
{ "type": "mrkdwn", "text": "*Records processed:* 412" },
{ "type": "mrkdwn", "text": "*Records changed:* 38" },
{ "type": "mrkdwn", "text": "*Segments updated:* 4" },
{ "type": "mrkdwn", "text": "*Flagged for review:* 2" }
]},
{ "type": "context", "elements": [
{ "type": "mrkdwn", "text": "Run at 2024-06-16T10:05:00Z | <log_url|View full log>" }
]}
]
}Integration and API SpecPage 2 of 4
FS-DOC-05Technical
03Field mappings between tools
The following tables define the exact field-level mappings for each inter-agent data handoff. Use monospace field names exactly as shown. Any deviation from these names requires a coordinated update across the credential store config and the relevant agent.
Handoff 1: Typeform to Agent 1 (unified working dataset)
Source tool
Source field
Destination tool
Destination field
Typeform
`answers[field_001].email`
Working dataset
`email`
Typeform
`answers[field_002].text`
Working dataset
`full_name`
Typeform
`answers[field_003].phone_number`
Working dataset
`phone`
Typeform
`form_response.submitted_at`
Working dataset
`typeform_submitted_at`
Typeform
`form_response.form_id`
Working dataset
`source_form_id`
Handoff 2: HubSpot to Agent 1 (unified working dataset)
Source tool
Source field
Destination tool
Destination field
HubSpot
`properties.email`
Working dataset
`email`
HubSpot
`properties.firstname`
Working dataset
`first_name`
HubSpot
`properties.lastname`
Working dataset
`last_name`
HubSpot
`properties.phone`
Working dataset
`phone`
HubSpot
`properties.lifecyclestage`
Working dataset
`lifecycle_stage`
HubSpot
`properties.hs_lead_status`
Working dataset
`lead_status`
HubSpot
`properties.hs_segment_tag`
Working dataset
`segment_tag_current`
HubSpot
`properties.hs_suppression_flag`
Working dataset
`suppressed`
HubSpot
`id`
Working dataset
`hubspot_vid`
HubSpot
`properties.hs_last_modified_date`
Working dataset
`hs_last_modified`
Handoff 3: Agent 1 to Segment (identity resolution)
Source tool
Source field
Destination tool
Destination field
Working dataset
`hubspot_vid`
Segment identify
`userId` (prefixed: hs_{hubspot_vid})
Working dataset
`email`
Segment identify
`traits.email`
Working dataset
`first_name`
Segment identify
`traits.firstName`
Working dataset
`last_name`
Segment identify
`traits.lastName`
Working dataset
`phone`
Segment identify
`traits.phone`
Working dataset
`lifecycle_stage`
Segment identify
`traits.lifecycleStage`
Working dataset
`typeform_submitted_at`
Segment identify
`traits.typeformSubmittedAt`
Handoff 4: Agent 2 to Mailchimp (segment sync)
Source tool
Source field
Destination tool
Destination field
Working dataset
`email`
Mailchimp member
`email_address`
Working dataset
`first_name`
Mailchimp merge field
`FNAME`
Working dataset
`last_name`
Mailchimp merge field
`LNAME`
Working dataset
`phone`
Mailchimp merge field
`PHONE`
Working dataset
`segment_tag_new`
Mailchimp member
`tags[]` (array)
Working dataset
`suppressed`
Mailchimp member
`status` (suppressed=true -> unsubscribed; do not re-subscribe)
Working dataset
`hubspot_vid`
Mailchimp merge field
`HSID`
Handoff 5: Agent 2 to HubSpot (write-back)
Source tool
Source field
Destination tool
Destination field
Working dataset
`segment_tag_new`
HubSpot contact
`properties.hs_segment_tag`
Working dataset
`suppressed`
HubSpot contact
`properties.hs_suppression_flag`
Working dataset
`dedup_merged_from_vids`
HubSpot contact
`properties.hs_dedup_merged_from`
Working dataset
`run_timestamp`
HubSpot contact
`properties.hs_last_segment_run`
Working dataset
`flagged_for_review`
HubSpot contact
`properties.hs_review_flag`
Handoff 6: Agent 3 to Google Sheets (run log)
Source tool
Source field
Destination tool
Destination field
Run summary
`run_timestamp`
Google Sheets
Column A: `run_timestamp`
Run summary
`agent_name`
Google Sheets
Column B: `agent_name`
Run summary
`records_processed`
Google Sheets
Column C: `records_processed`
Run summary
`records_changed`
Google Sheets
Column D: `records_changed`
Run summary
`segments_updated`
Google Sheets
Column E: `segments_updated`
Run summary
`flagged_count`
Google Sheets
Column F: `flagged_for_review`
Run summary
`run_status`
Google Sheets
Column G: `run_status`
Run summary
`notes`
Google Sheets
Column H: `notes`
Integration and API SpecPage 3 of 4
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Three discrete workflows, one per agent. Each workflow is independently triggerable and version-controlled. Shared credential store is referenced by all three workflows using environment variable injection at runtime. No credentials are hardcoded in workflow definitions.
Agent 1 trigger mechanism
Dual trigger: (1) inbound webhook from HubSpot contact.creation / contact.propertyChange events (real-time), and (2) inbound webhook from Typeform form_response events (real-time). A daily scheduled poll at 02:00 UTC acts as a backfill safety net using the HubSpot search API filtered by hs_last_modified_date > now-25h. Webhook signature validation is enforced before any payload is processed: HubSpot uses X-HubSpot-Signature-v3 (HMAC-SHA256 of client secret + method + URI + body); Typeform uses Typeform-Signature header (sha256=HMAC of body using TYPEFORM_WEBHOOK_SECRET). Reject and log any request that fails signature validation with HTTP 401.
Agent 2 trigger mechanism
Chained trigger from Agent 1 via an internal orchestration event emitted on successful deduplication completion. Agent 2 does not expose a public webhook. The trigger carries the run_id and a pointer to the clean dataset in the orchestration layer's temporary data store. Agent 2 also subscribes to the daily schedule (02:05 UTC) to catch any dataset written by the Agent 1 backfill poll.
Agent 3 trigger mechanism
Chained trigger from Agent 2 via an internal orchestration event emitted on completion of the Mailchimp sync and HubSpot write-back. Carries the run summary payload (segment counts, changed record count, flagged contact list). No external webhook or poll. Single execution per Agent 2 run; no parallelism.
Temporary data store
The orchestration layer's built-in key-value or blob store holds the merged working dataset between Agent 1 and Agent 2. Keys are namespaced by run_id (e.g. run:{run_id}:dataset). TTL is set to 6 hours. If Agent 2 does not consume the dataset within the TTL, the run is marked failed and an alert is sent to SLACK_ALERT_CHANNEL_ID.
Run concurrency control
A run lock (mutex keyed on the process ID 'segmentation-list-mgmt') prevents concurrent executions of the full pipeline. If a lock is held when a new trigger fires, the new trigger is queued for up to 30 minutes and then executed sequentially. This prevents race conditions on HubSpot batch writes and Mailchimp audience updates.
Environment
Staging and production are separate orchestration workspaces with separate credential stores. All integration tests must pass in staging before any credential or workflow change is promoted to production. Promotion requires a manual approval step documented in the Test and QA Plan.
The following code block lists every credential that must be present in the credential store before any workflow is activated. No value below should ever appear in workflow source code, version control, or log output.
Credential store: all keys required before workflow activation
// Credential store contents (all required before first run)
// HubSpot
HUBSPOT_PRIVATE_APP_TOKEN=<hubspot_private_app_token>
HUBSPOT_PORTAL_ID=<hubspot_portal_id>
HUBSPOT_WEBHOOK_CLIENT_SECRET=<hubspot_webhook_client_secret>
// Typeform
TYPEFORM_API_TOKEN=<typeform_personal_access_token>
TYPEFORM_FORM_ID=<target_form_id>
TYPEFORM_WEBHOOK_SECRET=<typeform_webhook_signing_secret>
// Segment
SEGMENT_WRITE_KEY=<segment_source_write_key>
SEGMENT_API_TOKEN=<segment_public_api_token>
SEGMENT_SPACE_ID=<segment_unify_space_id>
SEGMENT_MAILCHIMP_DESTINATION_ID=<segment_mailchimp_destination_id>
// Mailchimp
MAILCHIMP_API_KEY=<mailchimp_api_key>
MAILCHIMP_DC=<data_centre_suffix_e.g._us6>
MAILCHIMP_DEFAULT_LIST_ID=<master_audience_id>
MAILCHIMP_AUDIENCE_HIGH_VALUE_ID=<audience_id>
MAILCHIMP_AUDIENCE_LAPSED_ID=<audience_id>
MAILCHIMP_AUDIENCE_NEWSLETTER_ONLY_ID=<audience_id>
// Add one entry per additional segment audience
// Google Sheets
GOOGLE_SERVICE_ACCOUNT_JSON=<json_key_as_single_line_string>
GSHEETS_LOG_SPREADSHEET_ID=<spreadsheet_id>
GSHEETS_LOG_SHEET_NAME=<tab_name_e.g._RunLog>
// Slack
SLACK_BOT_TOKEN=<xoxb_bot_token>
SLACK_CHANNEL_ID=<marketing_ops_channel_id>
SLACK_ALERT_CHANNEL_ID=<ops_alerts_channel_id>
// Orchestration internal
PIPELINE_LOCK_KEY=segmentation-list-mgmt
DATASET_TTL_SECONDS=21600
RUN_BACKFILL_LOOKBACK_HOURS=25
05Error handling and retry logic
Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently: every failure path must write to the run log, post an alert to SLACK_ALERT_CHANNEL_ID, and halt the affected run rather than proceeding with incomplete data. Retry logic uses exponential backoff with jitter unless the error is explicitly non-retryable.
Integration
Scenario
Required behaviour
HubSpot webhook inbound
Invalid or missing X-HubSpot-Signature-v3 header
Return HTTP 401 immediately. Do not process payload. Log the raw header value and source IP to the run log. No retry. Alert SLACK_ALERT_CHANNEL_ID if more than 3 invalid signatures are received within 10 minutes.
HubSpot Contacts API (read)
HTTP 429 rate limit exceeded
Pause execution for the duration specified in the Retry-After response header. If no header is present, wait 10 seconds. Retry up to 5 times with exponential backoff (10s, 20s, 40s, 80s, 160s). If all retries fail, abort the run, log the failed contact IDs, and alert the ops channel.
HubSpot Contacts API (batch write)
HTTP 400 on one or more records in a batch
Extract the failed record IDs from the response errors array. Log each failed ID with the error message. Retry the failed records individually (not as a batch) after a 5-second delay. If individual retry also fails with 400, mark the record as write_failed in the run log and continue processing remaining records. Do not abort the entire run.
Typeform webhook inbound
Typeform-Signature header validation fails
Return HTTP 401 immediately. Log the event_id and timestamp. Do not process. If the same form_response event arrives via the backfill poll later, it will be processed through normal validation. No immediate retry.
Typeform Responses API (backfill poll)
HTTP 5xx or connection timeout
Retry up to 3 times with exponential backoff (15s, 30s, 60s). If all retries fail, log the failure and skip the backfill for this run. The next scheduled poll (following day) will catch missed records via the lookback window. Alert the ops channel.
Segment identify call
HTTP 5xx from Segment ingest endpoint
Retry up to 3 times with backoff (5s, 15s, 45s). If all retries fail, buffer the failed identify payloads in the orchestration data store (TTL 24 hours) and retry once on the next scheduled run. Log each buffered record. Alert if buffer exceeds 50 records.
Segment Profiles API
Profile not found (404) after identify call
Wait 60 seconds (eventual consistency window) and retry the Profiles API call once. If still 404, proceed with the data as-is from the working dataset without Segment identity resolution. Log the email as unresolved_profile. Do not halt the run.
Mailchimp batch sync
HTTP 400 on a member update (e.g. attempting to re-subscribe an unsubscribed contact)
Catch the 400 error per operation in the batch response. Mark the contact as permanently_suppressed in the run log. Do not retry. Write the suppressed flag back to the HubSpot record. Continue processing remaining batch operations.
Mailchimp suppression list poll
HTTP 5xx or timeout
Retry up to 3 times with backoff (10s, 30s, 90s). If all retries fail, abort the segment sync step entirely for this run. Do not proceed with syncing audiences without the suppression list, as this risks contacting suppressed contacts. Log and alert immediately.
Google Sheets append
HTTP 5xx or quota exceeded (429)
Retry up to 3 times with backoff (5s, 15s, 30s). If all retries fail, write the run summary payload to the orchestration data store as a pending_log entry (TTL 48 hours) and attempt to append again on the next run. Post the run summary to Slack regardless of Sheets failure so the team is not left uninformed.
Slack chat.postMessage
HTTP 429 (rate limit) or channel_not_found error
For 429: retry after the Retry-After header value (typically 1 second). For channel_not_found: log the error with the SLACK_CHANNEL_ID value and alert via SLACK_ALERT_CHANNEL_ID if it is different. Do not retry channel_not_found; it requires manual intervention to fix the channel ID in the credential store.
Full pipeline
Dataset TTL expires before Agent 2 consumes it (Agent 1 completes but Agent 2 does not start within 6 hours)
The orchestration layer detects the expired key on the next Agent 2 trigger attempt. Mark the run as failed_ttl_expired in the run log. Do not attempt to re-run Agent 1 automatically. Alert SLACK_ALERT_CHANNEL_ID with the run_id and timestamp. Requires manual investigation before restarting.
Unhandled exceptions must never fail silently. If an exception is caught that does not match any row in this table, the orchestration layer must: (1) log the full stack trace and run context to the run log, (2) post an alert to SLACK_ALERT_CHANNEL_ID with the exception type and run_id, and (3) halt the current run. Swallowing exceptions or continuing a run after an unknown error is not acceptable behaviour in any agent.
For any questions about this specification, contact the FullSpec team at support@gofullspec.com.
Integration and API SpecPage 4 of 4