FS-DOC-05Technical
Integration and API Spec
Paid Ad Campaign Management
[YourCompany.com] · Marketing Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for every integration point in the Paid Ad Campaign Management automation. It covers tool authentication, required OAuth scopes, webhook and polling configuration, field mappings between agents, credential storage conventions, rate limits, and error handling requirements. The FullSpec team uses this specification to build, connect, and validate every integration. Your team uses it as the reference for any future changes, credential rotations, or platform upgrades.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Orchestration layer
Hosts and executes all agent workflows; manages scheduling, retry logic, and credential injection
Internal service account
N/A (platform choice confirmed at build)
All agents
Google Ads
Source of search and shopping campaign spend, clicks, impressions, and conversion data
OAuth 2.0 + developer token
Standard Access developer token (free, requires application)
Agent 1 (Ad Data Sync)
Meta Ads Manager
Source of Facebook and Instagram campaign spend and performance metrics
OAuth 2.0 via Meta Business SDK; system user token under Business Manager
Meta Business Manager with active ad account; system user at Admin level
Agent 1 (Ad Data Sync)
Google Sheets
Master performance log; budget and KPI threshold configuration table; anomaly flag store
OAuth 2.0 (Google Workspace service account or delegated user token)
Any Google account (Workspace recommended for service account support)
Agent 1 (Ad Data Sync), Agent 2 (Campaign Analysis and Alert)
Slack
Real-time anomaly alert delivery to the marketing channel
Bot token via Slack OAuth 2.0 app install
Free tier sufficient; Slack Business+ required if message retention or audit logs are needed
Agent 2 (Campaign Analysis and Alert)
HubSpot
Conversion attribution write destination; contact and deal property updates
Private app token (HubSpot OAuth 2.0 private app)
HubSpot Marketing Hub Starter or above (contacts API required)
Agent 2 (Campaign Analysis and Alert)
Google Looker Studio
Automated reporting dashboard connected to the Google Sheets master log
Google service account with Viewer/Editor access to the linked sheet; no direct API call from automation
Free (Looker Studio is free with a Google account)
Agent 2 (Campaign Analysis and Alert) triggers data refresh via Sheets write
Before you connect anything: sandbox-test every integration using test credentials, a staging Google Sheet, a Slack test channel, and HubSpot sandbox environment before any production credentials are entered. Never write production developer tokens or access tokens directly into workflow configuration fields. All credentials must live in the shared credential store only. Confirm that the Google Ads developer token has been approved at Standard Access level before build begins; Basic Access blocks the reporting API endpoints required here.
Integration and API SpecPage 1 of 4
FS-DOC-05Technical
02Per-tool integration detail
Google Ads
Queried daily by the Ad Data Sync Agent to retrieve spend, clicks, impressions, and conversions for all active campaigns. Uses the Google Ads Query Language (GAQL) via the REST reporting endpoint. Developer token must be at Standard Access level to pull data across all campaigns without restrictions.
Auth method
OAuth 2.0. A service account or delegated user credential is used. The refresh token is stored in the credential store under the key GOOGLE_ADS_REFRESH_TOKEN. The developer token is stored under GOOGLE_ADS_DEVELOPER_TOKEN. Both are injected at runtime; neither is hardcoded.
Required scopes
https://www.googleapis.com/auth/adwords
Trigger / poll setup
Polled on a daily schedule at 09:00 local time. There is no webhook available from Google Ads. The orchestration layer initiates the pull. The customer ID (stored as GOOGLE_ADS_CUSTOMER_ID in the credential store) is used in every request path.
Required configuration
GOOGLE_ADS_CUSTOMER_ID stored in credential store. GAQL query targets campaigns with status = ENABLED. Date range is set to YESTERDAY for each daily pull. No hardcoded customer IDs or date strings in the workflow nodes.
Rate limits
Google Ads Reporting API: 15,000 requests per day per developer token at Standard Access. The automation issues one GAQL request per run covering all active campaigns in a single query. At 20 active campaigns and one run per day, the daily request count is approximately 1 to 3 requests depending on pagination. Throttling is not required at current volume. Monitor daily request count if campaign volume grows beyond 500 rows per response page.
Constraints
Standard Access developer token required before build starts. Basic Access returns only test account data. The token approval process with Google can take 3 to 5 business days; this must be initiated before week 1 of the build schedule. MCC (manager account) access is recommended if multiple ad accounts are in scope.
// GAQL query (stored as workflow variable, not hardcoded)
SELECT campaign.id, campaign.name, campaign.status,
metrics.cost_micros, metrics.clicks,
metrics.impressions, metrics.conversions
FROM campaign
WHERE segments.date DURING YESTERDAY
AND campaign.status = 'ENABLED'
// Output: array of campaign objects -> passed to Sheets write node
Meta Ads Manager
Queried daily in parallel with Google Ads by the Ad Data Sync Agent. Uses the Meta Marketing API (Graph API v18.0 or later) to pull spend, impressions, clicks, and conversions for all active Facebook and Instagram campaigns under the configured ad account. Requires a system user token created in Meta Business Manager.
Auth method
OAuth 2.0 system user token. A system user is created in Meta Business Manager at Admin level and assigned to the ad account. The long-lived system user access token (60-day expiry, must be refreshed proactively) is stored under META_SYSTEM_USER_TOKEN in the credential store. The ad account ID is stored under META_AD_ACCOUNT_ID.
Required scopes
ads_read, ads_management, business_management, read_insights
Trigger / poll setup
Polled on the same daily schedule as Google Ads (09:00 local time), initiated by the orchestration layer. No webhook is available for scheduled reporting pulls. The date_preset parameter is set to yesterday for each request.
Required configuration
META_AD_ACCOUNT_ID stored in credential store (format: act_XXXXXXXXXX). API version pinned to v18.0 or above in the base URL. Fields requested: campaign_id, campaign_name, spend, impressions, clicks, actions (for conversions). No hardcoded account IDs in any workflow node.
Rate limits
Meta Marketing API uses a score-based rate limit system. Each call consumes points based on data volume. Standard business accounts receive a base allocation of 200 points per hour per token. A single daily insights call for 20 campaigns consumes approximately 5 to 10 points. Throttling is not required at current volume. If the API returns error code 17 (user request limit reached), the retry handler must apply exponential backoff starting at 60 seconds.
Constraints
System user token expires every 60 days. A token refresh reminder must be built into the monitoring process or automated via the Meta token refresh endpoint. The automation must not use a personal user access token; system user tokens are required for stable production use. Business Manager must have the ad account assigned to the system user before token generation.
// Meta Insights API call (parameters stored as workflow variables)
GET /{META_AD_ACCOUNT_ID}/insights
?fields=campaign_id,campaign_name,spend,impressions,clicks,actions
&date_preset=yesterday
&level=campaign
&access_token={META_SYSTEM_USER_TOKEN}
// Output: array of campaign insight objects -> passed to Sheets write nodeGoogle Sheets
Used by both agents. The Ad Data Sync Agent writes daily campaign metrics to the master log tab. The Campaign Analysis and Alert Agent reads from the master log and from a separate thresholds configuration tab. Looker Studio connects directly to the sheet as a data source and refreshes automatically when new rows are written.
Auth method
OAuth 2.0 via Google service account. The service account JSON key is stored under GOOGLE_SHEETS_SERVICE_ACCOUNT_KEY in the credential store. The service account is granted Editor access to the master log spreadsheet. The spreadsheet ID is stored under GOOGLE_SHEETS_SPREADSHEET_ID.
Required scopes
https://www.googleapis.com/auth/spreadsheets, https://www.googleapis.com/auth/drive.readonly (drive.readonly required only if the spreadsheet ID is resolved by name rather than direct ID)
Trigger / poll setup
No webhook. The Agent 2 workflow polls the master log sheet for the latest written row timestamp after a configurable delay (default 2 minutes) following Agent 1 completion, or is triggered directly by Agent 1 passing a completion signal via the orchestration layer.
Required configuration
Sheet structure: Tab 1 named campaign_daily_log with columns in this exact order: date (ISO 8601), platform, campaign_id, campaign_name, spend_usd, impressions, clicks, conversions, cpc_usd, anomaly_flag (boolean), anomaly_reason (string). Tab 2 named budget_thresholds with columns: campaign_id, monthly_budget_usd, cpc_ceiling_usd, daily_spend_pct_alert (integer 0-100), min_daily_conversions. All threshold values are edited in the sheet, never in the workflow.
Rate limits
Google Sheets API: 300 requests per minute per project; 60 requests per minute per user. The automation writes a batch of up to 20 rows per run using a single batchUpdate call. One batchUpdate per agent per day. Rate limit is not a concern at current volume.
Constraints
The sheet must not be renamed after the spreadsheet ID is stored in the credential store. Tab names (campaign_daily_log and budget_thresholds) must not be changed without updating the corresponding workflow variables. Looker Studio connects to the sheet by spreadsheet ID and tab name; renaming either breaks the dashboard data source.
// Write payload from Agent 1 (batchUpdate, values array)
range: 'campaign_daily_log!A:K'
values: [[date, platform, campaign_id, campaign_name,
spend_usd, impressions, clicks, conversions,
cpc_usd, anomaly_flag, anomaly_reason]]
// Read payload for Agent 2 (threshold lookup)
range: 'budget_thresholds!A:E'
// Returns: campaign_id -> threshold map for comparison logicSlack
Used by the Campaign Analysis and Alert Agent to send formatted anomaly alerts to the designated marketing Slack channel. Each alert names the campaign, the metric that triggered the flag, the current value, the threshold, and the recommended action for the manager.
Auth method
Slack Bot Token (xoxb-...) via OAuth 2.0 Slack app install. The app is installed to the workspace with Bot Token Scopes only. Token is stored under SLACK_BOT_TOKEN in the credential store. Channel ID (not channel name) is stored under SLACK_ALERT_CHANNEL_ID.
Required scopes
chat:write, chat:write.public (if the bot is not a member of the channel), channels:read (for channel ID resolution during setup only)
Webhook / trigger setup
No inbound webhook required. Slack is an output-only integration. The automation uses the chat.postMessage API method. Incoming webhooks are not used (bot token approach provides more control and consistent identity).
Required configuration
SLACK_ALERT_CHANNEL_ID stored in credential store (format: C0XXXXXXXXX). Message payload uses Block Kit JSON for structured formatting: a header block with the campaign name, a section block with metric name, current value, threshold, and recommended action, and a context block with the alert timestamp. Block Kit template is stored as a workflow variable, not hardcoded in the node.
Rate limits
Slack Web API: 1 request per second per method by default (Tier 3: chat.postMessage allows up to 1 message per second). At current volume, a maximum of 20 alert messages could be sent in a single run (one per campaign if all flagged). At 1 message per second this completes in under 20 seconds. No throttling required. If volume increases, batch alerts into a single message using Block Kit sections.
Constraints
The bot must be invited to the alert channel before token is stored. Using channel name instead of channel ID will cause failures if the channel is renamed. If no anomalies are detected in a run, no Slack message is sent (no false-positive noise). The message payload must not include raw API credentials or PII.
// chat.postMessage payload (Block Kit, abbreviated)
{
channel: {SLACK_ALERT_CHANNEL_ID},
blocks: [
{ type: 'header', text: { type: 'plain_text',
text: 'Campaign Alert: {campaign_name}' }},
{ type: 'section', fields: [
{ type: 'mrkdwn', text: '*Metric:* {anomaly_reason}' },
{ type: 'mrkdwn', text: '*Value:* {current_value}' },
{ type: 'mrkdwn', text: '*Threshold:* {threshold_value}' },
{ type: 'mrkdwn', text: '*Action:* {recommended_action}' }
]},
{ type: 'context', elements: [
{ type: 'mrkdwn', text: 'Alert generated: {run_timestamp}' }
]}
]
}HubSpot
Used by the Campaign Analysis and Alert Agent to write conversion attribution data to HubSpot contact and deal records. Conversions captured in the ad platform APIs are matched to HubSpot contacts via UTM source and email where available, and the deal source properties are updated to reflect the paid channel.
Auth method
HubSpot private app token (Bearer token). The private app is created in HubSpot under Settings > Integrations > Private Apps. The token is stored under HUBSPOT_PRIVATE_APP_TOKEN in the credential store. No OAuth redirect flow is used.
Required scopes
crm.objects.contacts.read, crm.objects.contacts.write, crm.objects.deals.read, crm.objects.deals.write, crm.schemas.contacts.read
Trigger / poll setup
No inbound webhook from HubSpot. The automation writes to HubSpot as an output step. The contact lookup uses the HubSpot Search API filtered by hs_analytics_source_data_1 (UTM source) or email where a direct match is available from the conversion event.
Required configuration
HUBSPOT_PRIVATE_APP_TOKEN in credential store. HUBSPOT_PORTAL_ID in credential store. Custom contact properties required: hs_analytics_source (standard), hs_analytics_source_data_1 (UTM source, standard), paid_ad_platform (custom, single-line text), last_conversion_campaign_id (custom, single-line text), last_conversion_date (custom, date property). Custom properties must be created in HubSpot before the build starts.
Rate limits
HubSpot API: 100 requests per 10 seconds for private apps on Starter and above. Attribution writes are batched using the Batch Contacts Update endpoint (up to 100 contacts per call). At 20 campaigns and an estimated 10 to 50 daily conversions, the attribution step issues 1 to 2 batch calls per run. No throttling required at current volume.
Constraints
UTM parameters must be consistently applied in all ad destination URLs before go-live. Inconsistent or missing UTMs will result in unmatched contacts and incomplete attribution. A UTM audit should be completed during the access and configuration week before the build begins. If no matching contact is found for a conversion event, the event is written to a staging tab in Google Sheets (attribution_unmatched) for manual review rather than silently dropped.
// HubSpot Batch Contact Update (PATCH /crm/v3/objects/contacts/batch/update)
inputs: [
{
id: {hubspot_contact_id},
properties: {
paid_ad_platform: '{platform}',
last_conversion_campaign_id: '{campaign_id}',
last_conversion_date: '{conversion_date_iso}',
hs_analytics_source: 'PAID_SEARCH' | 'PAID_SOCIAL',
hs_analytics_source_data_1: '{utm_source}'
}
}
]
// Unmatched conversions -> written to Google Sheets attribution_unmatched tabGoogle Looker Studio
The Looker Studio dashboard connects directly to the Google Sheets master log as its data source. No direct API call is made by the automation to Looker Studio. The dashboard refreshes automatically each time the orchestration layer writes new rows to the sheet. Configuration is handled at the Looker Studio level, not within the workflow.
Auth method
The Looker Studio report is connected to the Google Sheet via the owner's Google account or the service account granted Editor access. No additional credential is stored in the automation credential store for Looker Studio specifically.
Required scopes
No additional OAuth scopes required in the automation layer. Looker Studio uses its own Google account connection to read from the linked sheet.
Trigger / refresh setup
Looker Studio data sources have a configurable refresh frequency (minimum every 15 minutes for connected Google Sheets). The dashboard will reflect new data within 15 minutes of Agent 1 completing its Sheets write. No explicit API call is needed from the automation to trigger a refresh.
Required configuration
The Looker Studio report must be connected to the campaign_daily_log tab of the Google Sheet by spreadsheet ID. The data source field types must be configured: date as Date, spend_usd as Currency (USD), impressions and clicks as Number, cpc_usd as Currency (USD), anomaly_flag as Boolean. Report-level date range filter should default to Last 30 days. The report URL is stored in the LOOKER_STUDIO_REPORT_URL variable for reference in alerts if needed.
Rate limits
Not applicable. Looker Studio refresh is managed by Google infrastructure. No API rate limit applies to the automation layer for this integration.
Constraints
If the Google Sheet tab name (campaign_daily_log) is changed, the Looker Studio data source connection will break and must be reconnected manually. The Looker Studio report should be shared with all stakeholders who need dashboard access before go-live so they do not require the Sheets URL directly.
// No direct API call from automation to Looker Studio
// Data flow: Agent 1 writes to Google Sheets campaign_daily_log
// Looker Studio reads campaign_daily_log on its own refresh cycle (<= 15 min)
// Dashboard fields consumed:
// date, platform, campaign_name, spend_usd,
// impressions, clicks, conversions, cpc_usd, anomaly_flag
Integration and API SpecPage 2 of 4
FS-DOC-05Technical
03Field mappings between tools
The tables below define every field mapping at each agent handoff point. Field names are shown in exact monospace format as they appear in the API response or sheet column header. All transformations are noted inline.
Handoff 1: Google Ads API to Google Sheets master log (Agent 1, Ad Data Sync Agent)
Source tool
Source field
Destination tool
Destination field
Transformation
Google Ads
segments.date
Google Sheets
date
ISO 8601 string, no change
Google Ads
(literal string)
Google Sheets
platform
Hardcoded value: 'google_ads'
Google Ads
campaign.id
Google Sheets
campaign_id
Cast to string
Google Ads
campaign.name
Google Sheets
campaign_name
No transformation
Google Ads
metrics.cost_micros
Google Sheets
spend_usd
Divide by 1,000,000; round to 2 decimal places
Google Ads
metrics.impressions
Google Sheets
impressions
Cast to integer
Google Ads
metrics.clicks
Google Sheets
clicks
Cast to integer
Google Ads
metrics.conversions
Google Sheets
conversions
Round to nearest integer
Google Ads
metrics.cost_micros / metrics.clicks
Google Sheets
cpc_usd
Derived: (cost_micros / 1,000,000) / clicks; null if clicks = 0
Google Ads
(set by Agent 2 logic)
Google Sheets
anomaly_flag
Boolean; written FALSE by Agent 1; updated by Agent 2
Google Ads
(set by Agent 2 logic)
Google Sheets
anomaly_reason
Empty string written by Agent 1; populated by Agent 2
Handoff 2: Meta Ads Manager API to Google Sheets master log (Agent 1, Ad Data Sync Agent)
Source tool
Source field
Destination tool
Destination field
Transformation
Meta Ads Manager
date_start
Google Sheets
date
ISO 8601 string; use date_start for yesterday range
Meta Ads Manager
(literal string)
Google Sheets
platform
Hardcoded value: 'meta_ads'
Meta Ads Manager
campaign_id
Google Sheets
campaign_id
No transformation (already string)
Meta Ads Manager
campaign_name
Google Sheets
campaign_name
No transformation
Meta Ads Manager
spend
Google Sheets
spend_usd
Already in USD decimal; round to 2 decimal places
Meta Ads Manager
impressions
Google Sheets
impressions
Cast to integer
Meta Ads Manager
clicks
Google Sheets
clicks
Cast to integer
Meta Ads Manager
actions[action_type='offsite_conversion.fb_pixel_purchase'].value
Google Sheets
conversions
Extract purchase action count; default to 0 if action not present
Meta Ads Manager
spend / clicks
Google Sheets
cpc_usd
Derived: spend / clicks; null if clicks = 0
Meta Ads Manager
(set by Agent 2 logic)
Google Sheets
anomaly_flag
Boolean FALSE written by Agent 1
Meta Ads Manager
(set by Agent 2 logic)
Google Sheets
anomaly_reason
Empty string written by Agent 1
Handoff 3: Google Sheets master log to Campaign Analysis and Alert Agent (Agent 2 read)
Source tool
Source field
Destination tool
Destination field
Notes
Google Sheets
campaign_id
Agent 2 internal
campaign_id
Used as join key to budget_thresholds tab
Google Sheets
spend_usd
Agent 2 internal
actual_spend
Compared against daily_spend_pct_alert threshold
Google Sheets
cpc_usd
Agent 2 internal
actual_cpc
Compared against cpc_ceiling_usd threshold
Google Sheets
conversions
Agent 2 internal
actual_conversions
Compared against min_daily_conversions threshold
Google Sheets (budget_thresholds)
monthly_budget_usd
Agent 2 internal
budget_limit
Used to derive daily budget ceiling
Google Sheets (budget_thresholds)
cpc_ceiling_usd
Agent 2 internal
cpc_ceiling
Upper bound for CPC alert
Google Sheets (budget_thresholds)
daily_spend_pct_alert
Agent 2 internal
spend_pct_threshold
Percentage of daily budget that triggers an alert
Google Sheets (budget_thresholds)
min_daily_conversions
Agent 2 internal
conversion_floor
Minimum conversions before underperformance flag
Handoff 4: Agent 2 anomaly output to Google Sheets (anomaly flag update)
Source tool
Source field
Destination tool
Destination field
Notes
Agent 2 internal
anomaly_detected (boolean)
Google Sheets
anomaly_flag
Updates existing row for the campaign and date; uses PATCH / row update by campaign_id + date key
Agent 2 internal
anomaly_description (string)
Google Sheets
anomaly_reason
Human-readable reason string, e.g. 'CPC exceeded ceiling: $4.20 vs $3.00 limit'
Handoff 5: Agent 2 to HubSpot (conversion attribution write)
Source tool
Source field
Destination tool
Destination field
Notes
Google Ads / Meta (via Agent 2)
campaign_id
HubSpot
last_conversion_campaign_id
Custom contact property; string
Google Ads / Meta (via Agent 2)
platform
HubSpot
paid_ad_platform
Custom contact property; 'google_ads' or 'meta_ads'
Google Ads / Meta (via Agent 2)
segments.date or date_start
HubSpot
last_conversion_date
Custom date property; ISO 8601
Google Ads / Meta (via Agent 2)
utm_source (from conversion URL)
HubSpot
hs_analytics_source_data_1
Standard HubSpot property; extracted from UTM parameters on conversion URL
Google Ads / Meta (via Agent 2)
platform category
HubSpot
hs_analytics_source
PAID_SEARCH for Google Ads, PAID_SOCIAL for Meta Ads
Integration and API SpecPage 3 of 4
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Two discrete workflows, one per agent. Agent 1 (Ad Data Sync Agent) runs first on the daily schedule. Agent 2 (Campaign Analysis and Alert Agent) is triggered immediately after Agent 1 signals completion via an internal workflow status event or a shared flag written to the credential store (AGENT1_LAST_RUN_TIMESTAMP). A shared credential store is used by both workflows; credentials are injected at runtime and never stored in node configuration.
Agent 1 trigger mechanism
Time-based poll. Fires daily at 09:00 local time via the orchestration scheduler. No inbound webhook. If the scheduled run fails to start within a 5-minute window, the orchestration layer must raise an error alert to the nominated monitoring channel before the workflow is retried.
Agent 2 trigger mechanism
Event-based, fired by Agent 1 completion signal. Agent 2 listens for the AGENT1_COMPLETE flag in the shared credential store (or an internal workflow completion event depending on platform capability). A maximum wait timeout of 30 minutes is configured; if Agent 1 does not signal completion within this window, Agent 2 raises a timeout error and does not proceed. This prevents stale data from being analysed.
Credential store type
The orchestration platform's native encrypted secret store. All credentials referenced in the code block below are stored as named secrets. No credential appears in plain text in any workflow node, environment variable file committed to version control, or log output.
Looker Studio refresh
Passive. No orchestration step needed. Looker Studio refreshes its Google Sheets data source on its own 15-minute cycle after Agent 1 writes to the sheet.
Retry policy (global)
Maximum 3 retries per node with exponential backoff starting at 30 seconds (30s, 60s, 120s). If all retries are exhausted, the workflow transitions to a FAILED state and sends an error notification to support@gofullspec.com and the process owner. No silent failures permitted anywhere in the stack.
Credential rotation: The Meta system user token expires every 60 days. A calendar reminder must be set at the time of initial token generation to rotate it before expiry. If the token expires during a live run, the workflow will fail at the Meta API call with a 190 error code; the error handler (see section 05) must catch this and send an explicit 'token expired' alert rather than a generic failure message.
Credential store reference: all keys injected at runtime, never hardcoded
// Credential store contents (all values stored as encrypted secrets)
// Keys are referenced by name in all workflow nodes
GOOGLE_ADS_DEVELOPER_TOKEN // Google Ads Standard Access developer token
GOOGLE_ADS_REFRESH_TOKEN // OAuth 2.0 refresh token for Google Ads API
GOOGLE_ADS_CLIENT_ID // OAuth 2.0 client ID (from Google Cloud Console)
GOOGLE_ADS_CLIENT_SECRET // OAuth 2.0 client secret (from Google Cloud Console)
GOOGLE_ADS_CUSTOMER_ID // Google Ads customer ID (format: 123-456-7890)
META_SYSTEM_USER_TOKEN // Meta Business Manager system user access token (60-day TTL)
META_AD_ACCOUNT_ID // Meta ad account ID (format: act_XXXXXXXXXX)
META_APP_ID // Meta app ID from Meta Developer Console
META_APP_SECRET // Meta app secret from Meta Developer Console
GOOGLE_SHEETS_SERVICE_ACCOUNT_KEY // JSON key for Google service account (minified)
GOOGLE_SHEETS_SPREADSHEET_ID // Google Sheets spreadsheet ID (from sheet URL)
SLACK_BOT_TOKEN // Slack bot OAuth token (format: xoxb-...)
SLACK_ALERT_CHANNEL_ID // Slack channel ID for anomaly alerts (format: C0XXXXXXXXX)
HUBSPOT_PRIVATE_APP_TOKEN // HubSpot private app bearer token
HUBSPOT_PORTAL_ID // HubSpot portal (account) ID
LOOKER_STUDIO_REPORT_URL // Public or shared URL of the Looker Studio dashboard
AGENT1_LAST_RUN_TIMESTAMP // Written by Agent 1 on completion; read by Agent 2 trigger
AGENT1_COMPLETE // Boolean flag: 'true' when Agent 1 write is confirmed
05Error handling and retry logic
Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently. All errors must be logged to the orchestration platform's run history and, where noted, must also trigger an active notification. The retry policy in section 04 applies globally unless a specific override is noted below.
Integration
Scenario
Required behaviour
Google Ads API
Authentication failure (401 / invalid token)
Halt workflow. Attempt token refresh using stored refresh token. If refresh succeeds, retry the API call once. If refresh fails, send error alert to support@gofullspec.com and process owner with message 'Google Ads auth failure: manual token rotation required'. Do not retry further. Log full error response.
Google Ads API
Daily request quota exceeded (RESOURCE_EXHAUSTED)
Log the error and pause the Google Ads fetch node until the following day's quota reset (midnight Pacific time). Write a partial-run flag to the credential store. Send a Slack notification to the alert channel stating data for this platform will be missing from today's log. Do not fail the entire workflow; proceed with Meta data only.
Meta Ads Manager API
Token expired (error code 190)
Halt workflow immediately. Send explicit 'Meta system user token expired' alert to support@gofullspec.com and process owner. Do not retry with the expired token. Log the expiry event with timestamp. Manual token rotation is required before the next run. Mark today's run as INCOMPLETE in the credential store flag.
Meta Ads Manager API
Rate limit hit (error code 17, user request limit reached)
Apply exponential backoff: wait 60 seconds, then 120 seconds, then 240 seconds before retry. Maximum 3 retries. If all retries fail, log the error and proceed with data collected so far. Send an amber alert to the Slack channel noting partial Meta data for the day.
Google Sheets write (Agent 1)
Write fails (network error or permission denied)
Retry up to 3 times with 30s / 60s / 120s backoff. If all retries fail, the raw campaign data objects must be written to a backup JSON log in the orchestration platform's run history so no data is lost. Send error alert to support@gofullspec.com. Agent 2 must not proceed if Agent 1 write has not been confirmed.
Google Sheets read (Agent 2)
Threshold tab missing or columns renamed
Halt Agent 2 immediately. Send error alert: 'budget_thresholds tab structure error: check column headers match specification'. Do not proceed with analysis using incomplete threshold data. Log the discrepancy. No Slack campaign alerts should be sent from an incomplete analysis run.
Slack alert (Agent 2)
chat.postMessage fails (invalid channel, bot not in channel)
Log the error with the channel ID and error description. Retry once after 10 seconds. If retry fails, fall back to sending the alert as a direct message to the process owner's Slack user ID (stored as SLACK_FALLBACK_USER_ID in the credential store). If DM also fails, write the alert content to the Google Sheets campaign_daily_log anomaly_reason field and send an email alert to support@gofullspec.com.
HubSpot contact match (Agent 2)
No matching contact found for a conversion event
Do not drop the event. Write the unmatched conversion record to the Google Sheets attribution_unmatched tab with columns: date, platform, campaign_id, campaign_name, conversion_count, utm_source, failure_reason. Send a weekly digest (every Monday) of unmatched records to the process owner for manual review. Never silently discard a conversion record.
HubSpot API write (Agent 2)
Rate limit hit (429 Too Many Requests)
Apply exponential backoff: 10s, 20s, 40s. Retry up to 3 times. If all retries are exhausted, batch the remaining updates and store them in a retry queue in the credential store (HUBSPOT_RETRY_QUEUE). The queue is processed at the next scheduled run before new data is written. Log the queue length and alert support@gofullspec.com if the queue exceeds 50 records.
HubSpot API write (Agent 2)
Property does not exist (404 on custom property)
Halt the HubSpot write step immediately. Send error alert: 'HubSpot custom property missing: check paid_ad_platform, last_conversion_campaign_id, last_conversion_date exist in portal'. Log the missing property name from the API error response. Do not attempt to write to undefined properties. No contact records should be partially updated.
Agent 2 trigger
Agent 1 completion signal not received within 30-minute timeout
Agent 2 must not start analysis on stale data. Log the timeout event with the expected and actual timestamps. Send error alert to support@gofullspec.com and process owner: 'Agent 1 completion signal timeout: Agent 2 did not run today'. The daily log will have a gap for this date; this must be noted in the Google Sheets run_audit tab (date, status: TIMEOUT, reason).
Any integration
Unhandled exception (uncaught error type)
The orchestration layer must catch all unhandled exceptions at the workflow level. Write the full error object (type, message, stack trace, run ID, timestamp) to the platform's run history. Send an immediate alert to support@gofullspec.com with subject 'Unhandled exception: Paid Ad Campaign Management workflow'. Under no circumstances should a workflow node fail without producing a log entry. Silent failures are not acceptable.
Integration and API SpecPage 4 of 4