FS-DOC-05Technical
Integration and API Spec
Marketing Attribution Reporting
[YourCompany.com] · Marketing Department · Prepared by FullSpec · [Today's Date]
This document specifies every integration point required to automate the Marketing Attribution Reporting process across Google Ads, Meta Ads Manager, Google Analytics 4, HubSpot, Google Sheets, and Slack. It is written for the FullSpec build team and covers authentication methods, exact OAuth scopes, webhook and trigger configuration, field mappings between agents, credential store layout, and error handling behaviour. Nothing in this document should be changed without updating the corresponding credential store and notifying the process owner.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
Orchestration layer
Hosts all three agent workflows, manages scheduling, credential store, and inter-agent handoffs
Internal service account
Any hosted workflow automation tier with OAuth 2.0 support
All agents
Google Ads
Source of paid search spend, impressions, clicks, and conversion data
OAuth 2.0 (Google Identity)
Any active Google Ads account with API access enabled
Agent 1: Data Fetch Agent
Meta Ads Manager
Source of paid social spend, impressions, clicks, and conversions
OAuth 2.0 (Meta for Developers app)
Meta Business Suite with Marketing API access approved
Agent 1: Data Fetch Agent
Google Analytics 4
Source of session, source/medium, and goal completion data
OAuth 2.0 (Google Identity / service account)
GA4 property with Data API enabled (free)
Agent 1: Data Fetch Agent
HubSpot
Source of CRM deal and lead source data including closed revenue
OAuth 2.0 (HubSpot private app token preferred)
HubSpot Starter or above (API access included)
Agent 1: Data Fetch Agent
Google Sheets
Staging and master attribution output; read/write by two agents
OAuth 2.0 (Google Identity / service account)
Google Workspace or personal account (free tier sufficient)
Agent 2: Attribution and Merge Agent; Agent 3: Summary and Delivery Agent
Slack
Report delivery channel; receives formatted message and sheet link
OAuth 2.0 (Slack app bot token)
Slack Free or above (incoming webhooks available on all tiers)
Agent 3: Summary and Delivery Agent
Before you connect anything: configure and validate every integration against a sandbox or test environment before any production credentials are stored. For Google Ads and Meta, use a test account or a low-spend campaign set. For HubSpot, use a sandbox portal. For Google Sheets, use a staging copy of the master sheet. For Slack, use a private test channel. Only promote credentials to the production credential store after a clean end-to-end test has been logged in the QA plan.
Integration and API SpecPage 1 of 4
FS-DOC-05Technical
02Per-tool integration detail
Google Ads
The Google Ads API (v16+) is queried by the Data Fetch Agent to retrieve campaign, ad group, and conversion data for the reporting date range. Pagination is handled via page tokens. All requests use GAQL (Google Ads Query Language).
Auth method
OAuth 2.0 with refresh token. A Google Cloud project must be created, the Google Ads API enabled, and a developer token approved (basic access sufficient for <15k operations/day). The OAuth client ID, client secret, refresh token, and developer token are stored in the credential store, never hardcoded.
Required scopes
https://www.googleapis.com/auth/adwords
Trigger / fetch setup
Polled on schedule by the Data Fetch Agent. No webhook available. The agent passes the reporting window as date_range.start_date and date_range.end_date in GAQL. The customer ID (customer_id) for the target account is stored in the credential store.
Required configuration
Developer token stored in credential store. OAuth client ID and secret stored in credential store. MCC or standalone customer_id stored in credential store. Target GAQL fields: campaign.name, campaign.id, metrics.cost_micros, metrics.clicks, metrics.impressions, metrics.conversions, metrics.conversions_value, segments.date.
Rate limits
Basic access: 15,000 operations/day. Standard access: higher limits on request. At a volume of 4 to 6 reports/month with one GAQL query per run, current volume sits well below 100 operations/month. Throttling is not required. Exponential backoff should still be implemented for transient 429 or 500 errors.
Constraints
Developer token approval can take 2 to 5 business days. Test account usage requires a separate test customer ID. Data for the current day may be incomplete; always query for closed date ranges only. cost_micros must be divided by 1,000,000 to convert to dollar values.
// GAQL query example (Data Fetch Agent)
SELECT campaign.name, campaign.id,
metrics.cost_micros, metrics.clicks,
metrics.impressions, metrics.conversions,
metrics.conversions_value, segments.date
FROM campaign
WHERE segments.date BETWEEN '{{period_start}}' AND '{{period_end}}'
AND campaign.status = 'ENABLED'
ORDER BY segments.date ASC
// Output written to Google Sheets staging tab: gads_rawMeta Ads Manager
The Meta Marketing API (v19.0+) is queried by the Data Fetch Agent to retrieve campaign-level spend, impressions, clicks, and conversions for the reporting period. Results are paginated using cursor-based pagination.
Auth method
OAuth 2.0 via a registered Meta for Developers app. A long-lived System User access token is preferred over a short-lived user token to avoid expiry during scheduled runs. App ID, app secret, and system user access token are stored in the credential store.
Required scopes
ads_read, business_management, ads_management (ads_management required for insights endpoint access on some account configurations)
Trigger / fetch setup
Polled on schedule. The agent calls the /{ad_account_id}/insights endpoint with time_range set to the reporting window. The ad_account_id (act_XXXXXXXXXX format) is stored in the credential store. Async reporting via report_run_id should be used for date ranges exceeding 90 days.
Required configuration
app_id stored in credential store. app_secret stored in credential store. system_user_access_token stored in credential store. ad_account_id stored in credential store. Fields requested: campaign_name, campaign_id, spend, impressions, clicks, actions (purchase, lead), action_values, date_start, date_stop.
Rate limits
Marketing API rate limits are usage-based (BUC score). Standard app tier: 200 score units per hour per token. Each insights call costs approximately 1 to 3 units. At 4 to 6 report runs/month, expected usage is under 20 units/month. Throttling is not required at current volume, but the agent must check the x-business-use-case-usage header and back off if the score exceeds 75%.
Constraints
Meta app review is required for ads_read scope on a new app and can take 5 to 10 business days. System user tokens must be regenerated before expiry (60 days for short-lived; system user tokens do not expire but must be stored securely). Attribution window settings in Meta (1d_click, 7d_click, 28d_click) must be agreed and locked in configuration before build.
// Meta Insights API call (Data Fetch Agent)
GET /{ad_account_id}/insights
?fields=campaign_name,campaign_id,spend,impressions,
clicks,actions,action_values,date_start,date_stop
&time_range={"since":"{{period_start}}","until":"{{period_end}}"}
&level=campaign
&time_increment=1
&limit=500
// Output written to Google Sheets staging tab: meta_rawGoogle Analytics 4
The Google Analytics Data API (GA4) is queried by the Data Fetch Agent to retrieve session counts, source/medium breakdown, and key event (conversion) totals for the reporting period.
Auth method
OAuth 2.0 or service account JSON key. A service account is preferred for unattended scheduled runs. The service account must be granted Viewer access to the GA4 property. Service account key JSON (or the key fields: client_email, private_key) is stored in the credential store.
Required scopes
https://www.googleapis.com/auth/analytics.readonly
Trigger / fetch setup
Polled on schedule. The agent calls the properties/{property_id}/runReport endpoint. The GA4 property_id (numeric, not the UA tracking ID) is stored in the credential store. Date ranges are passed as startDate and endDate in ISO 8601 format.
Required configuration
service_account_client_email stored in credential store. service_account_private_key stored in credential store. ga4_property_id stored in credential store. Dimensions requested: sessionDefaultChannelGrouping, sessionSourceMedium, date. Metrics requested: sessions, keyEvents, totalRevenue (if e-commerce enabled).
Rate limits
GA4 Data API: 10 concurrent requests per property; 1,000 requests per day per property (standard quota). At 4 to 6 report runs/month, well under quota. No throttling required. Implement retry with backoff for quota-exceeded (429) responses.
Constraints
GA4 data for the current day is subject to processing latency of up to 24 to 48 hours. Always query closed date ranges. The property_id must be the numeric GA4 property ID, not a UA ID. Sampling may apply on free GA4 properties for high-traffic sites; verify row counts against the GA4 UI during QA.
// GA4 Data API runReport body (Data Fetch Agent)
{
"dateRanges": [{"startDate": "{{period_start}}", "endDate": "{{period_end}}"}],
"dimensions": [
{"name": "sessionDefaultChannelGrouping"},
{"name": "sessionSourceMedium"},
{"name": "date"}
],
"metrics": [
{"name": "sessions"},
{"name": "keyEvents"},
{"name": "totalRevenue"}
]
}
// Output written to Google Sheets staging tab: ga4_rawHubSpot
The HubSpot CRM API is queried by the Data Fetch Agent to retrieve closed deals and lead source data for the reporting period, providing the bottom-of-funnel revenue layer for attribution.
Auth method
Private app access token (preferred over OAuth 2.0 for server-side scheduled runs). The private app token is generated in HubSpot under Settings > Integrations > Private Apps and stored in the credential store. Token does not expire unless revoked.
Required scopes
crm.objects.deals.read, crm.objects.contacts.read, crm.schemas.deals.read
Trigger / fetch setup
Polled on schedule. The agent calls POST /crm/v3/objects/deals/search with a filter on closedate within the reporting period and dealstage equal to the configured closed-won stage ID. The closed-won stage ID (e.g. closedwon) is stored in the credential store, not hardcoded, as it varies by HubSpot portal pipeline.
Required configuration
hubspot_private_app_token stored in credential store. hubspot_pipeline_id stored in credential store. hubspot_closed_won_stage_id stored in credential store. Properties fetched: dealname, closedate, amount, hs_analytics_source, hs_analytics_source_data_1, hs_analytics_source_data_2, hs_deal_stage_probability, associated_contact_ids.
Rate limits
HubSpot API: 100 requests/10 seconds per token (Starter and above). Search endpoint: 4 requests/second. At 4 to 6 report runs/month with one search call per run plus pagination calls for large deal sets, current volume is negligible. No throttling required. Implement retry with backoff for 429 responses.
Constraints
HubSpot deal source accuracy depends on CRM data hygiene. If hs_analytics_source is blank, the agent must log the gap and attribute the deal to 'Unknown Source' rather than dropping it. Pagination via after cursor is required if the deal count exceeds 100 per page. The HubSpot portal ID must be confirmed before build.
// HubSpot deal search request body (Data Fetch Agent)
POST /crm/v3/objects/deals/search
{
"filterGroups": [{
"filters": [
{"propertyName": "closedate", "operator": "BETWEEN",
"value": "{{period_start_epoch_ms}}", "highValue": "{{period_end_epoch_ms}}"},
{"propertyName": "dealstage", "operator": "EQ",
"value": "{{hubspot_closed_won_stage_id}}"}
]
}],
"properties": ["dealname","closedate","amount",
"hs_analytics_source","hs_analytics_source_data_1",
"hs_analytics_source_data_2"],
"limit": 100,
"after": "{{pagination_cursor}}"
}
// Output written to Google Sheets staging tab: hubspot_rawGoogle Sheets
Google Sheets serves as both the staging area for raw fetched data (written by the Data Fetch Agent) and the master attribution output (written and read by the Attribution and Merge Agent, then read by the Summary and Delivery Agent). A single spreadsheet with named tabs is used.
Auth method
OAuth 2.0 or service account JSON key. The same service account used for GA4 can be granted Editor access to the target spreadsheet. Service account key fields (client_email, private_key) are stored in the credential store.
Required scopes
https://www.googleapis.com/auth/spreadsheets, https://www.googleapis.com/auth/drive.file (drive.file is sufficient if the spreadsheet was created by the service account; use drive if accessing a pre-existing sheet)
Trigger / fetch setup
No webhook used. The Attribution and Merge Agent is triggered by a completion status flag written to the control_flags tab by the Data Fetch Agent. The Summary and Delivery Agent reads a similar flag written by the Attribution and Merge Agent. The spreadsheet ID is stored in the credential store.
Required configuration
sheets_spreadsheet_id stored in credential store. Tab structure: gads_raw (columns: date, campaign_name, campaign_id, spend, clicks, impressions, conversions, conversion_value), meta_raw (date, campaign_name, campaign_id, spend, impressions, clicks, purchases, leads, action_value), ga4_raw (date, channel_grouping, source_medium, sessions, key_events, revenue), hubspot_raw (deal_name, close_date, amount, source, source_data_1, source_data_2), attribution_output (channel, spend, clicks, conversions, revenue, cpa, roas, attribution_model), control_flags (agent_name, status, timestamp, row_count).
Rate limits
Sheets API: 300 requests/minute per project; 60 requests/minute per user. Batch writes (batchUpdate, values.batchUpdate) must be used to stay within limits. At current volume, a single report run generates under 20 API calls across all agents. Throttling is not required.
Constraints
The spreadsheet must be created and shared with the service account email before first run. Named ranges should be set for the header row of each tab to make schema changes explicit. The control_flags tab must never be manually edited during a live run. Cell formula-based attribution logic must be documented separately in the Developer Handover Pack.
// Sheets tab: control_flags schema
| agent_name | status | timestamp | row_count |
|-------------------------|------------|----------------------|-----------|
| data_fetch_agent | complete | 2024-05-12T08:03:11Z | 142 |
| attribution_merge_agent | complete | 2024-05-12T08:07:44Z | 5 |
| summary_delivery_agent | in_progress| 2024-05-12T08:08:01Z | - |
// Attribution and Merge Agent reads: gads_raw, meta_raw, ga4_raw, hubspot_raw
// Attribution and Merge Agent writes: attribution_output, control_flags
// Summary and Delivery Agent reads: attribution_output, control_flags
Slack
Slack receives the final formatted report message and Google Sheets link from the Summary and Delivery Agent via an incoming webhook or bot token post to the configured channel.
Auth method
OAuth 2.0 Slack app with bot token (xoxb- prefix). Create a Slack app in the target workspace, add the bot to the destination channel, and store the bot token in the credential store. Alternatively, use a per-channel incoming webhook URL stored in the credential store.
Required scopes
chat:write, chat:write.public (if posting to channels the bot is not a member of), incoming-webhook (if using webhook method instead of bot token)
Webhook / trigger setup
Outbound only. No inbound webhook required. The agent posts via POST to chat.postMessage (bot token method) or to the incoming webhook URL. The slack_channel_id (e.g. C0XXXXXXXX) and slack_bot_token are stored in the credential store.
Required configuration
slack_bot_token stored in credential store. slack_channel_id stored in credential store. sheets_report_url stored in credential store (or generated dynamically from sheets_spreadsheet_id). Message template: plain-text summary block + attachment with sheet link. The Slack app must be installed in the workspace and invited to the target channel before first run.
Rate limits
Slack Web API: 1 message/second per channel (Tier 3 rate limit for chat.postMessage). At 4 to 6 posts/month, no throttling is needed. The agent must handle 429 responses with a Retry-After header-based delay.
Constraints
The bot must be a member of the destination channel. If the channel is private, the bot must be explicitly invited. Slack message blocks are limited to 3,000 characters per block. The summary generated by the Summary and Delivery Agent must be truncated or split if it approaches this limit. Do not hardcode the channel name; always use the channel ID.
// Slack chat.postMessage payload (Summary and Delivery Agent)
POST https://slack.com/api/chat.postMessage
{
"channel": "{{slack_channel_id}}",
"text": "Attribution report ready for {{period_label}}",
"blocks": [
{"type": "section", "text": {"type": "mrkdwn",
"text": "*Attribution Report: {{period_label}}*\n{{summary_text}}"}},
{"type": "section", "text": {"type": "mrkdwn",
"text": "<{{sheets_report_url}}|Open full report in Google Sheets>"}}
]
}Integration and API SpecPage 2 of 4
FS-DOC-05Technical
03Field mappings between tools
The following tables cover every agent handoff in the automated flow. Exact field names are shown as they appear in each tool's API response or sheet column header. The Attribution and Merge Agent is responsible for normalising all source fields to the unified schema in the attribution_output tab.
Handoff 1: Google Ads API to Google Sheets (gads_raw tab) -- Data Fetch Agent
Source tool
Source field
Destination tool
Destination field
Google Ads
segments.date
Google Sheets (gads_raw)
date
Google Ads
campaign.name
Google Sheets (gads_raw)
campaign_name
Google Ads
campaign.id
Google Sheets (gads_raw)
campaign_id
Google Ads
metrics.cost_micros / 1000000
Google Sheets (gads_raw)
spend
Google Ads
metrics.clicks
Google Sheets (gads_raw)
clicks
Google Ads
metrics.impressions
Google Sheets (gads_raw)
impressions
Google Ads
metrics.conversions
Google Sheets (gads_raw)
conversions
Google Ads
metrics.conversions_value
Google Sheets (gads_raw)
conversion_value
Handoff 2: Meta Ads Manager API to Google Sheets (meta_raw tab) -- Data Fetch Agent
Source tool
Source field
Destination tool
Destination field
Meta Ads Manager
date_start
Google Sheets (meta_raw)
date
Meta Ads Manager
campaign_name
Google Sheets (meta_raw)
campaign_name
Meta Ads Manager
campaign_id
Google Sheets (meta_raw)
campaign_id
Meta Ads Manager
spend
Google Sheets (meta_raw)
spend
Meta Ads Manager
impressions
Google Sheets (meta_raw)
impressions
Meta Ads Manager
clicks
Google Sheets (meta_raw)
clicks
Meta Ads Manager
actions[action_type=purchase].value
Google Sheets (meta_raw)
purchases
Meta Ads Manager
actions[action_type=lead].value
Google Sheets (meta_raw)
leads
Meta Ads Manager
action_values[action_type=purchase].value
Google Sheets (meta_raw)
action_value
Handoff 3: Google Analytics 4 API to Google Sheets (ga4_raw tab) -- Data Fetch Agent
Source tool
Source field
Destination tool
Destination field
Google Analytics 4
dimensions[date].value
Google Sheets (ga4_raw)
date
Google Analytics 4
dimensions[sessionDefaultChannelGrouping].value
Google Sheets (ga4_raw)
channel_grouping
Google Analytics 4
dimensions[sessionSourceMedium].value
Google Sheets (ga4_raw)
source_medium
Google Analytics 4
metrics[sessions].value
Google Sheets (ga4_raw)
sessions
Google Analytics 4
metrics[keyEvents].value
Google Sheets (ga4_raw)
key_events
Google Analytics 4
metrics[totalRevenue].value
Google Sheets (ga4_raw)
revenue
Handoff 4: HubSpot API to Google Sheets (hubspot_raw tab) -- Data Fetch Agent
Source tool
Source field
Destination tool
Destination field
HubSpot
properties.dealname
Google Sheets (hubspot_raw)
deal_name
HubSpot
properties.closedate
Google Sheets (hubspot_raw)
close_date
HubSpot
properties.amount
Google Sheets (hubspot_raw)
amount
HubSpot
properties.hs_analytics_source
Google Sheets (hubspot_raw)
source
HubSpot
properties.hs_analytics_source_data_1
Google Sheets (hubspot_raw)
source_data_1
HubSpot
properties.hs_analytics_source_data_2
Google Sheets (hubspot_raw)
source_data_2
Handoff 5: Google Sheets (staging tabs) to Google Sheets (attribution_output tab) -- Attribution and Merge Agent
Source tool
Source field
Destination tool
Destination field
Google Sheets (gads_raw)
campaign_name
Google Sheets (attribution_output)
channel
Google Sheets (gads_raw)
spend
Google Sheets (attribution_output)
spend
Google Sheets (gads_raw)
clicks
Google Sheets (attribution_output)
clicks
Google Sheets (gads_raw)
conversions
Google Sheets (attribution_output)
conversions
Google Sheets (gads_raw)
conversion_value
Google Sheets (attribution_output)
revenue
Google Sheets (meta_raw)
spend
Google Sheets (attribution_output)
spend (summed by channel)
Google Sheets (meta_raw)
purchases + leads
Google Sheets (attribution_output)
conversions (summed)
Google Sheets (ga4_raw)
channel_grouping
Google Sheets (attribution_output)
channel (normalised)
Google Sheets (ga4_raw)
key_events
Google Sheets (attribution_output)
conversions (cross-referenced)
Google Sheets (hubspot_raw)
source
Google Sheets (attribution_output)
channel (CRM layer)
Google Sheets (hubspot_raw)
amount
Google Sheets (attribution_output)
revenue (CRM layer)
Google Sheets (attribution_output)
spend / conversions
Google Sheets (attribution_output)
cpa
Google Sheets (attribution_output)
revenue / spend
Google Sheets (attribution_output)
roas
Handoff 6: Google Sheets (attribution_output) to Slack -- Summary and Delivery Agent
Source tool
Source field
Destination tool
Destination field
Google Sheets (attribution_output)
channel (top row by roas)
Slack message
Top-performing channel in summary_text
Google Sheets (attribution_output)
cpa (all channels)
Slack message
Per-channel CPA lines in summary_text
Google Sheets (attribution_output)
roas (all channels)
Slack message
Per-channel ROAS lines in summary_text
Google Sheets (attribution_output)
anomaly_flag (if set)
Slack message
Anomaly alert line in summary_text
Google Sheets (control_flags)
timestamp
Slack message
Report generation timestamp in summary_text
Credential store
sheets_report_url
Slack message
sheets_report_url in block link
Integration and API SpecPage 3 of 4
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Three separate workflow definitions, one per agent: (1) data_fetch_workflow, (2) attribution_merge_workflow, (3) summary_delivery_workflow. All three share a single credential store. Inter-agent handoffs are mediated by the control_flags tab in Google Sheets, not by direct workflow chaining, to allow independent retry of any agent without cascading restarts.
Agent 1 trigger: Data Fetch Agent
Schedule-based poll. Fires on the configured cron expression (e.g. weekly: 0 6 * * MON, monthly: 0 6 1 * *). No inbound webhook. The orchestration platform evaluates the schedule and initiates the data_fetch_workflow. A manual trigger input is also supported for on-demand campaign-end runs.
Agent 2 trigger: Attribution and Merge Agent
Poll-based on control_flags. The attribution_merge_workflow polls the control_flags tab every 2 minutes after the scheduled run window opens. It proceeds only when the data_fetch_agent row shows status = 'complete' and row_count > 0. This avoids race conditions if the Data Fetch Agent run is delayed.
Agent 3 trigger: Summary and Delivery Agent
Poll-based on control_flags. The summary_delivery_workflow polls control_flags every 2 minutes after the attribution merge window opens. It proceeds only when the attribution_merge_agent row shows status = 'complete'. The agent then reads attribution_output and posts to Slack.
Signature validation
No inbound webhooks are used in this automation (all triggers are outbound polls or schedules). Outbound requests to Google, Meta, HubSpot, and Slack APIs use HTTPS exclusively. Slack's signing secret is not required for outbound-only bot token posts but must be stored if inbound Slack events are added in a future iteration.
Credential store
All secrets are stored in the orchestration platform's encrypted credential store. No credential is written to code, sheet cells, or log outputs. See the code block below for the full list of credential keys.
Environment separation
A staging credential set (prefixed STAGING_) is maintained separately from the production set. Staging credentials point to test ad accounts, the GA4 test property, a HubSpot sandbox portal, a staging Google Sheet, and a private Slack test channel. The staging set is used for all development and QA runs.
All keys above must exist in the orchestration platform credential store before any workflow is activated. Never write these values to sheet cells, logs, or code.
// Credential store -- full key list
// Google Ads
GADS_CLIENT_ID = "<oauth_client_id>"
GADS_CLIENT_SECRET = "<oauth_client_secret>"
GADS_REFRESH_TOKEN = "<oauth_refresh_token>"
GADS_DEVELOPER_TOKEN = "<developer_token>"
GADS_CUSTOMER_ID = "<customer_id_without_dashes>"
// Meta Ads Manager
META_APP_ID = "<meta_app_id>"
META_APP_SECRET = "<meta_app_secret>"
META_SYSTEM_USER_TOKEN = "<long_lived_system_user_access_token>"
META_AD_ACCOUNT_ID = "act_XXXXXXXXXX"
META_ATTRIBUTION_WINDOW = "7d_click"
// Google Analytics 4
GA4_SERVICE_ACCOUNT_EMAIL = "<service_account>@<project>.iam.gserviceaccount.com"
GA4_SERVICE_ACCOUNT_KEY = "<private_key_pem_string>"
GA4_PROPERTY_ID = "<numeric_property_id>"
// HubSpot
HUBSPOT_PRIVATE_APP_TOKEN = "pat-na1-XXXXXXXXXXXXXXXXXXXXXXXX"
HUBSPOT_PORTAL_ID = "<portal_id>"
HUBSPOT_PIPELINE_ID = "<pipeline_id>"
HUBSPOT_CLOSED_WON_STAGE_ID = "closedwon"
// Google Sheets
SHEETS_SERVICE_ACCOUNT_EMAIL = "<same_as_GA4_or_separate>"
SHEETS_SERVICE_ACCOUNT_KEY = "<private_key_pem_string>"
SHEETS_SPREADSHEET_ID = "<spreadsheet_id_from_url>"
SHEETS_REPORT_URL = "https://docs.google.com/spreadsheets/d/<id>/edit"
// Slack
SLACK_BOT_TOKEN = "xoxb-XXXXXXXXXXXX-XXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXX"
SLACK_CHANNEL_ID = "C0XXXXXXXXX"
// Staging equivalents (prefix STAGING_ on all keys above)
// e.g. STAGING_GADS_CUSTOMER_ID, STAGING_SLACK_CHANNEL_ID, etc.
05Error handling and retry logic
Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently. If an error cannot be resolved by the retry logic below, the agent must write a failed status to the control_flags tab, halt downstream agents, and post an alert to the configured Slack error channel.
Integration
Scenario
Required behaviour
Google Ads API
401 Unauthorized (token expired or revoked)
Attempt token refresh using stored refresh token. If refresh fails, halt the data_fetch_workflow, write status = 'auth_failed' to control_flags, and post an alert to the Slack error channel. Do not proceed to downstream agents. Alert must include the timestamp and the failing credential key name.
Google Ads API
429 or 503 transient error
Retry up to 3 times with exponential backoff: 30 seconds, 90 seconds, 270 seconds. If all retries fail, write status = 'fetch_failed' to control_flags and post an alert. Log the final HTTP status code and response body.
Meta Ads Manager API
App review pending or ads_read scope not granted
On a 403 Permissions Error, halt immediately. Write status = 'scope_error' to control_flags and post a Slack alert stating which scope is missing. Do not retry automatically. Requires manual intervention to approve the Meta app scope.
Meta Ads Manager API
BUC score exceeds 75% (rate limit approaching)
Read x-business-use-case-usage header on every response. If BUC score exceeds 75%, pause for 60 seconds before the next paginated call. If score exceeds 90%, stop pagination, write a partial_fetch flag to control_flags, and alert via Slack. Do not proceed with incomplete data.
Google Analytics 4 API
Data sampling detected or row count below expected threshold
After the report run, compare returned rowCount against a configurable minimum threshold (default: 10 rows). If below threshold, write an anomaly_flag to the attribution_output tab and include a warning line in the Slack summary. Do not halt; proceed with available data but flag for human review.
Google Analytics 4 API
Property ID not found or service account lacks access
On a 403 or 404 response, halt the data_fetch_workflow immediately. Write status = 'config_error' to control_flags and post an alert specifying the GA4 property ID in use. This is a configuration error and must not be retried automatically.
HubSpot API
Empty deal result set (no closed deals in period)
An empty result set is a valid response. Write zero rows to hubspot_raw and set a zero_deals_flag in control_flags. The Attribution and Merge Agent must handle zero HubSpot rows gracefully without crashing. Include a note in the Slack summary that no closed deals were found for the period.
HubSpot API
429 rate limit on search endpoint
Retry after the Retry-After header value (default 10 seconds if header absent). Retry up to 5 times with a maximum wait of 60 seconds per retry. If all retries exhausted, write status = 'fetch_failed' to control_flags and alert via Slack. Log the page cursor so a manual resume is possible.
Google Sheets (write)
Spreadsheet ID not found or service account lacks Editor access
On a 404 or 403 response from the Sheets API, halt all downstream agents immediately. Write an error to the orchestration platform log and post an alert to Slack. This is a configuration error. The spreadsheet must be recreated and re-shared with the service account before the next run.
Google Sheets (control_flags poll)
Upstream agent status remains 'in_progress' beyond timeout threshold
If the control_flags poll finds status = 'in_progress' for more than 30 minutes past the expected completion window, the downstream agent must halt and post a timeout alert to Slack. The alert must name the stalled agent and the last known timestamp. Do not proceed with stale or incomplete data.
Slack (chat.postMessage)
Bot token invalid or bot not a member of the channel
On a 401 or channel_not_found error, log the failure to the orchestration platform and attempt one retry after 10 seconds. If the second attempt fails, write status = 'delivery_failed' to control_flags. The report data in Google Sheets is still complete; the failure is delivery-only. Alert the process owner via the registered fallback email (support@gofullspec.com can be used during build; replace with process owner email at handoff).
Any agent
Unhandled exception or unexpected response schema
Any exception not covered by a specific rule above must be caught by a global error handler in each workflow. The handler writes status = 'unhandled_error' and the exception message to control_flags, halts the current workflow, prevents downstream agents from starting, and posts an alert to Slack with the full exception text. Unhandled exceptions must never fail silently.
All error alerts posted to Slack must use a dedicated error channel (separate from the report delivery channel) so that operational failures are not mixed with report outputs. Configure SLACK_ERROR_CHANNEL_ID as a separate credential store entry before go-live.
Integration and API SpecPage 4 of 4