FS-DOC-05Technical
Integration and API Spec
Competitor Monitoring Automation
[YourCompany.com] · Marketing Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for every integration point in the Competitor Monitoring automation. It covers tool authentication, exact API scopes, webhook and trigger configuration, field mappings between agents, orchestration architecture, credential storage requirements, and error handling behaviour. The FullSpec team uses this document to build, configure, and validate all connections. It is intended for developer and technical-build audiences only.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
Orchestration layer
Workflow scheduling, agent sequencing, credential management, retry logic
Internal (platform-managed)
N/A (build platform)
All
SEMrush
Fetch competitor keyword rankings, traffic estimates, and paid ad keywords via REST API
API key (header)
Guru or above (API access required)
Agent 1: Data Collection Agent
Ahrefs
Retrieve new backlinks and recently published competitor content via REST API
API key (Bearer token)
Standard or above (API units plan)
Agent 1: Data Collection Agent
Google Alerts
Collect news and web mentions for each tracked competitor via RSS feed polling
None (public RSS)
Free (Google account required to create alerts)
Agent 1: Data Collection Agent
Google Sheets
Append weekly competitor data rows to the central tracking log; read competitor list as source of truth
OAuth 2.0 (service account or user OAuth)
Free (Google Workspace or personal account)
Agent 2: Intelligence Summariser Agent
Slack
Post formatted weekly intelligence digest to the designated channel
OAuth 2.0 (Bot token via Slack App)
Free or paid (Bot API available on all tiers)
Agent 2: Intelligence Summariser Agent
HubSpot
Log a note against the relevant marketing record summarising the week's competitive landscape
OAuth 2.0 (private app token)
Free CRM or above (Notes API included)
Agent 2: Intelligence Summariser Agent
Before you connect anything: provision and validate every credential in a sandbox or test environment before touching production. For SEMrush and Ahrefs, use a test API key with read-only permissions. For Google Sheets, Slack, and HubSpot, create a dedicated test workspace, sheet, and channel respectively. Do not store production credentials in the workflow until all test-environment runs pass QA.
02Per-tool integration detail
SEMrush
REST API v3. Used by Agent 1 (Data Collection Agent) to pull keyword position changes, estimated organic traffic, and new paid search keywords for each competitor domain on a weekly schedule.
Auth method
API key passed as the query parameter `key` on every request. Example: GET https://api.semrush.com/?type=domain_ranks&key={{SEMRUSH_API_KEY}}&domain=competitor.com&database=us
Required scopes
SEMrush API keys carry account-level access; no OAuth scope strings apply. The key must belong to an account on the Guru plan or above to unlock the API. Restrict the key to read-only endpoints via the SEMrush account dashboard under Profile > API Keys.
Webhook / trigger
No webhook. The orchestration layer polls SEMrush on the weekly schedule trigger (Monday 07:00). One request per competitor domain per endpoint called.
Required configuration
Store SEMRUSH_API_KEY in the credential store (see Section 04). The competitor domain list is read dynamically from the Google Sheets source-of-truth tab (column: competitor_domain). The target database code (e.g. 'us', 'uk') must be set as a workflow-level variable SEMRUSH_DATABASE and not hardcoded.
Rate limits
Guru plan: 3,000 API units/day. Each domain_ranks call costs 10 units; domain_organic costs 10 units. At 5 competitors with 2 endpoint types each, the weekly run costs 100 units. No throttling is needed at current volume. If the competitor list grows past 100 domains, implement a 500 ms delay between calls.
Constraints
API is read-only. Historical data beyond 36 months requires an add-on. Do not call the API more than once per competitor per run to conserve units. Any 429 response must trigger the exponential backoff defined in Section 05.
// Endpoint called per competitor
GET https://api.semrush.com/?type=domain_ranks&key={{SEMRUSH_API_KEY}}&domain={{competitor_domain}}&database={{SEMRUSH_DATABASE}}
GET https://api.semrush.com/?type=domain_organic&key={{SEMRUSH_API_KEY}}&domain={{competitor_domain}}&database={{SEMRUSH_DATABASE}}&display_limit=10
// Response fields extracted
Db, Domain, Rank, OrKw, OrTr, AdKw, AdTr
// Staged output fields
semrush.domain | semrush.organic_keywords | semrush.organic_traffic | semrush.paid_keywords | semrush.rankAhrefs
REST API v3 (api.ahrefs.com). Used by Agent 1 (Data Collection Agent) to retrieve new backlinks and recently published content for each tracked competitor domain.
Auth method
Bearer token in the Authorization header. Example: Authorization: Bearer {{AHREFS_API_TOKEN}}. Token is generated in the Ahrefs account under API > Access tokens.
Required scopes
Ahrefs API tokens are unscoped at issuance; access is governed by the account plan. The following endpoints must be accessible: /v3/site-explorer/new-backlinks, /v3/site-explorer/pages. Confirm the account is on Standard or API units plan. Store the token with read-only label in the credential store.
Webhook / trigger
No webhook. Agent 1 calls Ahrefs immediately after the SEMrush step completes, within the same weekly scheduled run.
Required configuration
Store AHREFS_API_TOKEN in the credential store. Competitor domains are passed from the same Google Sheets source list used by the SEMrush step. The date_from parameter must be computed dynamically as today minus 7 days (ISO 8601 format). Do not hardcode any domain or date.
Rate limits
Standard plan: 500 API rows/month included; additional rows billed per unit. At 5 competitors with 2 endpoint calls each (max 20 rows returned per call), the weekly run uses approximately 200 rows/month. No throttling is needed at current volume. Apply a 1 s inter-request delay to avoid 429 responses.
Constraints
The /v3/site-explorer endpoints require the target domain to be verified or queried as an external domain. External domain queries are supported on Standard plan. Do not query more than 50 rows per call. Any 403 response indicates a plan or token issue and must alert via Slack immediately (see Section 05).
// Endpoints called per competitor
GET https://api.ahrefs.com/v3/site-explorer/new-backlinks?target={{competitor_domain}}&date_from={{run_date_minus_7d}}&limit=20
GET https://api.ahrefs.com/v3/site-explorer/pages?target={{competitor_domain}}&date_from={{run_date_minus_7d}}&limit=20
// Response fields extracted
url_from | domain_rating_source | anchor | url_to | first_seen
url | title | words | traffic | value
// Staged output fields
ahrefs.domain | ahrefs.new_backlinks[] | ahrefs.new_pages[] | ahrefs.domain_rating_changeGoogle Alerts (RSS)
Public RSS feeds generated by Google Alerts. Used by Agent 1 (Data Collection Agent) to collect news and web mentions for each tracked competitor keyword since the last run.
Auth method
None. Google Alerts RSS feeds are publicly accessible via a URL generated by the Google Alerts UI. No credential is required. The alert must be created under a Google account with delivery set to RSS.
Required scopes
N/A. No OAuth or API key involved.
Webhook / trigger
The orchestration layer fetches each RSS URL via HTTP GET on the weekly schedule. The feed URL format is: https://www.google.com/alerts/feeds/{{GOOGLE_ALERTS_USER_ID}}/{{ALERT_ID}}. All feed URLs are stored in the credential store as GOOGLE_ALERTS_FEEDS (a JSON array of objects with keys: competitor_name, feed_url).
Required configuration
Each competitor must have a corresponding Google Alert configured in the Google account used for this automation. Alert frequency must be set to 'As it happens' or 'At most once a week'. Feed URLs are stored in the credential store and not hardcoded in the workflow. Filter items by pub_date to retain only entries published since the last run timestamp.
Rate limits
No documented rate limit. Google may throttle excessive polling from a single IP. At 5 competitors polled once per week, no throttling is required. Do not poll more than once per run per feed.
Constraints
RSS feed content is plain text and HTML-encoded. Strip HTML tags before passing items to the AI summariser. If a feed returns zero items, log an empty-result event but do not treat it as an error. Feed URLs expire if the underlying Google Alert is deleted; validate feed availability during the monitoring check step.
// HTTP GET per competitor alert feed
GET {{google_alerts_feed_url}}
// XML fields parsed per entry
<entry>
<title> | <link href> | <published> | <content>
</entry>
// Staged output fields (per item)
alerts.competitor_name | alerts.headline | alerts.url | alerts.published_at | alerts.snippetGoogle Sheets
Google Sheets API v4. Used by Agent 2 (Intelligence Summariser Agent) for two purposes: reading the competitor source list at the start of each run, and appending a new dated row of findings per competitor after the digest is generated.
Auth method
OAuth 2.0. Use a service account with a JSON key file for server-to-server access, or a user OAuth token if the orchestration platform requires it. Store the service account key or refresh token as GOOGLE_SHEETS_CREDENTIAL in the credential store.
Required scopes
https://www.googleapis.com/auth/spreadsheets (read and write to sheets). Do not request drive-level scope unless the orchestration platform requires file creation.
Webhook / trigger
No webhook outbound. The workflow reads the competitor list at run start and appends rows after digest generation. Both are synchronous HTTP calls initiated by the orchestration layer.
Required configuration
Store GOOGLE_SHEETS_SPREADSHEET_ID in the credential store. Do not hardcode the sheet ID. The spreadsheet must contain two named tabs: 'Competitors' (source list, columns: competitor_name, competitor_domain, is_active) and 'Weekly Log' (append target, columns: run_date, competitor_name, organic_keywords, organic_traffic, new_backlinks, new_pages, alert_mentions, flag_significant, digest_excerpt). Column order must match exactly. The 'Competitors' tab is the source of truth; if is_active is FALSE, the competitor is skipped.
Rate limits
Google Sheets API: 300 read requests/minute per project; 300 write requests/minute per project. At 5 competitors with one append each, the run makes 6 API calls total (1 read + 5 appends). No throttling is needed at current volume.
Constraints
Do not use batchUpdate for structural changes during an automated run. Append only to the 'Weekly Log' tab. If the spreadsheet is open in a browser during the run, writes may conflict; schedule the run during off-hours (07:00 Monday is acceptable). Any 403 response must trigger an alert and halt the append step without corrupting existing rows.
// Read competitor list
GET https://sheets.googleapis.com/v4/spreadsheets/{{GOOGLE_SHEETS_SPREADSHEET_ID}}/values/Competitors!A2:C
// Append weekly log row per competitor
POST https://sheets.googleapis.com/v4/spreadsheets/{{GOOGLE_SHEETS_SPREADSHEET_ID}}/values/Weekly Log!A:J:append
Body: { valueInputOption: 'USER_ENTERED', values: [[run_date, competitor_name, organic_keywords, organic_traffic, new_backlinks, new_pages, alert_mentions, flag_significant, digest_excerpt]] }
// Staged input fields consumed
semrush.organic_keywords | semrush.organic_traffic | ahrefs.new_backlinks[] | ahrefs.new_pages[] | alerts.headline | ai_summariser.flag_significant | ai_summariser.digest_excerptSlack
Slack Web API (chat.postMessage). Used by Agent 2 (Intelligence Summariser Agent) to post the formatted weekly intelligence digest to the designated marketing channel, with highlights in the main message and per-competitor detail in a thread.
Auth method
OAuth 2.0 Bot token (xoxb-...). Create a Slack App in the target workspace, grant the required scopes, install to workspace, and store the bot token as SLACK_BOT_TOKEN in the credential store.
Required scopes
chat:write (post messages to channels the bot is a member of). channels:read (resolve channel name to ID). Optional: files:write (if attaching a formatted digest PDF in future). The bot must be invited to the target channel before the first run.
Webhook / trigger
No inbound webhook. The workflow calls chat.postMessage after the Google Sheets append step completes. Use the Blocks Kit format for structured message rendering. The channel ID is stored as SLACK_CHANNEL_ID in the credential store.
Required configuration
Store SLACK_BOT_TOKEN and SLACK_CHANNEL_ID in the credential store. The channel must exist and the bot must be a member. The message template uses Block Kit with a Header block (weekly date), a Section block (highlights list from ai_summariser.highlights[]), and thread replies per competitor. Do not hardcode the channel name; always resolve by stored ID.
Rate limits
Slack Tier 3 rate limit: 50 requests/minute for chat.postMessage. The weekly run posts 1 main message plus up to 5 thread replies (one per competitor). Total: 6 API calls per run. No throttling required. Apply a 200 ms delay between thread replies to avoid burst issues.
Constraints
Block Kit message payload must not exceed 3,000 characters per block. Truncate any competitor section that exceeds 2,800 characters and append '... [see full log in Google Sheets]'. The bot token must not be rotated without updating the credential store; token rotation will silently break delivery. Monitor for 'channel_not_found' and 'not_in_channel' error codes.
// Main digest message
POST https://slack.com/api/chat.postMessage
Headers: { Authorization: Bearer {{SLACK_BOT_TOKEN}} }
Body: { channel: {{SLACK_CHANNEL_ID}}, blocks: [...highlight_blocks], text: 'Weekly Competitor Digest - {{run_date}}' }
// Per-competitor thread reply
POST https://slack.com/api/chat.postMessage
Body: { channel: {{SLACK_CHANNEL_ID}}, thread_ts: {{parent_message_ts}}, text: '{{competitor_name}}: {{competitor_section_text}}' }
// Fields consumed from AI summariser
ai_summariser.digest_date | ai_summariser.highlights[] | ai_summariser.competitor_sections[]{name, body, flag_significant}HubSpot
HubSpot CRM API v3 (Engagements / Notes). Used by Agent 2 (Intelligence Summariser Agent) to log a note against the marketing campaign or company record after the Slack post, keeping the CRM context current for sales and marketing alignment.
Auth method
Private App token (Bearer). Create a Private App in the HubSpot account under Settings > Integrations > Private Apps. Store the token as HUBSPOT_PRIVATE_APP_TOKEN in the credential store. Do not use legacy API keys (deprecated).
Required scopes
crm.objects.notes.write (create note engagements). crm.objects.companies.read (resolve company record IDs). If associating to a deal: crm.objects.deals.read. Grant only the scopes listed; do not enable contacts.write or crm.objects.contacts.write unless the process is extended.
Webhook / trigger
No inbound webhook. The workflow calls the Notes API after the Slack post step. The HubSpot company record IDs for each tracked competitor are stored in the credential store (see HUBSPOT_COMPETITOR_RECORD_MAP below). One note is created per competitor per weekly run.
Required configuration
Store HUBSPOT_PRIVATE_APP_TOKEN in the credential store. Store HUBSPOT_COMPETITOR_RECORD_MAP as a JSON object mapping competitor_name to hubspot_company_id (e.g. {"CompetitorA": "12345678"}). This map must be maintained when competitors are added or removed. The note body must include: run_date, top keyword changes, new backlink count, flagged changes (if any), and a link to the Google Sheets log row. Do not hardcode any record IDs.
Rate limits
HubSpot API: 100 requests per 10 seconds (burst); 40,000 requests/day on free CRM. At 5 competitor notes per weekly run, the process uses 5 API calls/week. No throttling required at current volume.
Constraints
Note body is plain text or HTML; use plain text for reliability. Maximum note body length is 65,536 characters. If HUBSPOT_COMPETITOR_RECORD_MAP does not contain an entry for a competitor, log a warning and skip that note without halting the run. Any 401 response indicates token expiry; alert via Slack immediately.
// Create note per competitor
POST https://api.hubapi.com/crm/v3/objects/notes
Headers: { Authorization: Bearer {{HUBSPOT_PRIVATE_APP_TOKEN}} }
Body: {
properties: {
hs_note_body: '{{note_body_text}}',
hs_timestamp: '{{run_timestamp_ms}}'
},
associations: [{ to: { id: {{hubspot_company_id}} }, types: [{ associationCategory: 'HUBSPOT_DEFINED', associationTypeId: 190 }] }]
}
// Fields consumed
ai_summariser.competitor_sections[]{name, body} | HUBSPOT_COMPETITOR_RECORD_MAP | run_date | sheets_log_urlIntegration and API SpecPage 1 of 3
FS-DOC-05Technical
03Field mappings between tools
The following tables document the exact field mappings at each agent handoff. Field names are shown in monospace format as they appear in the staged data object passed between steps. All field names are case-sensitive. Any field not listed here is dropped and not forwarded to the next step.
Handoff 1: SEMrush API response to Agent 1 staged dataset
Source tool
Source field
Destination tool
Destination field
SEMrush
`Domain`
Staged dataset (Agent 1)
`semrush.domain`
SEMrush
`OrKw`
Staged dataset (Agent 1)
`semrush.organic_keywords`
SEMrush
`OrTr`
Staged dataset (Agent 1)
`semrush.organic_traffic`
SEMrush
`AdKw`
Staged dataset (Agent 1)
`semrush.paid_keywords`
SEMrush
`Rank`
Staged dataset (Agent 1)
`semrush.rank`
Handoff 2: Ahrefs API response to Agent 1 staged dataset
Source tool
Source field
Destination tool
Destination field
Ahrefs
`url_from`
Staged dataset (Agent 1)
`ahrefs.new_backlinks[].url_from`
Ahrefs
`domain_rating_source`
Staged dataset (Agent 1)
`ahrefs.new_backlinks[].domain_rating`
Ahrefs
`anchor`
Staged dataset (Agent 1)
`ahrefs.new_backlinks[].anchor`
Ahrefs
`first_seen`
Staged dataset (Agent 1)
`ahrefs.new_backlinks[].first_seen`
Ahrefs
`url` (pages endpoint)
Staged dataset (Agent 1)
`ahrefs.new_pages[].url`
Ahrefs
`title`
Staged dataset (Agent 1)
`ahrefs.new_pages[].title`
Ahrefs
`traffic`
Staged dataset (Agent 1)
`ahrefs.new_pages[].traffic`
Handoff 3: Google Alerts RSS to Agent 1 staged dataset
Source tool
Source field
Destination tool
Destination field
Google Alerts RSS
`<title>` (entry)
Staged dataset (Agent 1)
`alerts.headline`
Google Alerts RSS
`<link href>` (entry)
Staged dataset (Agent 1)
`alerts.url`
Google Alerts RSS
`<published>`
Staged dataset (Agent 1)
`alerts.published_at`
Google Alerts RSS
`<content>` (stripped HTML)
Staged dataset (Agent 1)
`alerts.snippet`
Google Alerts RSS
Feed-level `competitor_name` (from GOOGLE_ALERTS_FEEDS config)
Staged dataset (Agent 1)
`alerts.competitor_name`
Handoff 4: Agent 1 staged dataset to Intelligence Summariser Agent (Agent 2) input
Source tool
Source field
Destination tool
Destination field
Staged dataset (Agent 1)
`semrush.domain`
Agent 2 AI prompt context
`input.competitor_name`
Staged dataset (Agent 1)
`semrush.organic_keywords`
Agent 2 AI prompt context
`input.seo.organic_keywords`
Staged dataset (Agent 1)
`semrush.organic_traffic`
Agent 2 AI prompt context
`input.seo.organic_traffic`
Staged dataset (Agent 1)
`semrush.paid_keywords`
Agent 2 AI prompt context
`input.seo.paid_keywords`
Staged dataset (Agent 1)
`ahrefs.new_backlinks[]`
Agent 2 AI prompt context
`input.backlinks.new_backlinks`
Staged dataset (Agent 1)
`ahrefs.new_pages[]`
Agent 2 AI prompt context
`input.content.new_pages`
Staged dataset (Agent 1)
`alerts.headline`
Agent 2 AI prompt context
`input.mentions[].headline`
Staged dataset (Agent 1)
`alerts.snippet`
Agent 2 AI prompt context
`input.mentions[].snippet`
Staged dataset (Agent 1)
`alerts.published_at`
Agent 2 AI prompt context
`input.mentions[].published_at`
Handoff 5: Agent 2 AI output to Google Sheets append
Source tool
Source field
Destination tool
Destination field
Agent 2 AI output
`ai_summariser.run_date`
Google Sheets (Weekly Log tab)
`run_date` (col A)
Agent 2 AI output
`ai_summariser.competitor_name`
Google Sheets (Weekly Log tab)
`competitor_name` (col B)
Staged dataset (Agent 1)
`semrush.organic_keywords`
Google Sheets (Weekly Log tab)
`organic_keywords` (col C)
Staged dataset (Agent 1)
`semrush.organic_traffic`
Google Sheets (Weekly Log tab)
`organic_traffic` (col D)
Staged dataset (Agent 1)
`ahrefs.new_backlinks[].length`
Google Sheets (Weekly Log tab)
`new_backlinks` (col E)
Staged dataset (Agent 1)
`ahrefs.new_pages[].length`
Google Sheets (Weekly Log tab)
`new_pages` (col F)
Staged dataset (Agent 1)
`alerts[].length`
Google Sheets (Weekly Log tab)
`alert_mentions` (col G)
Agent 2 AI output
`ai_summariser.flag_significant`
Google Sheets (Weekly Log tab)
`flag_significant` (col H)
Agent 2 AI output
`ai_summariser.digest_excerpt`
Google Sheets (Weekly Log tab)
`digest_excerpt` (col I)
Handoff 6: Agent 2 AI output to HubSpot note body
Source tool
Source field
Destination tool
Destination field
Agent 2 AI output
`ai_summariser.competitor_name`
HubSpot Notes API
`properties.hs_note_body` (prepended label)
Agent 2 AI output
`ai_summariser.competitor_sections[].body`
HubSpot Notes API
`properties.hs_note_body` (body text)
Orchestration layer
`run_timestamp_ms`
HubSpot Notes API
`properties.hs_timestamp`
Credential store
`HUBSPOT_COMPETITOR_RECORD_MAP[competitor_name]`
HubSpot Notes API
`associations[0].to.id`
Orchestration layer
`sheets_log_url`
HubSpot Notes API
`properties.hs_note_body` (appended footer link)
Integration and API SpecPage 2 of 3
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Two discrete workflows, one per agent. Workflow 1 (Data Collection Agent) runs on the weekly schedule trigger and stages its output to a shared intermediate data object. Workflow 2 (Intelligence Summariser Agent) is triggered by successful completion of Workflow 1. Both workflows share a single credential store. No workflow polls for credentials at runtime; all secrets are resolved from the credential store at execution start.
Agent 1 trigger mechanism
Scheduled poll. The orchestration platform fires Workflow 1 every Monday at 07:00 in the configured timezone (store as WORKFLOW_TIMEZONE, e.g. 'America/New_York'). No inbound webhook is used. If the schedule fires and the previous run has not yet completed, the new execution must be skipped (not queued) to prevent duplicate data collection.
Agent 2 trigger mechanism
Completion event from Agent 1. Workflow 2 is triggered by a success status event emitted by Workflow 1 after all three data sources have been collected and the staged dataset has been validated. Signature validation is not applicable here (internal trigger). If Workflow 1 emits a partial-success status (one source failed), Workflow 2 must still proceed but must annotate the digest with a data-gap warning for the missing source.
Shared credential store
All secrets and configuration values are stored in the orchestration platform's built-in encrypted credential store. Keys are referenced by name in workflow steps and resolved at runtime. See the code block below for the full credential store contents.
Intermediate data object
Agent 1 writes its staged output to a structured JSON object held in the orchestration platform's execution context or a dedicated in-memory store. This object is consumed by Agent 2 at the start of Workflow 2. The object is not persisted to an external database; it exists only for the duration of the combined run. If Agent 2 does not consume the object within 2 hours of Agent 1 completing, it is discarded and the run is marked failed.
Timezone and schedule config
All schedule-based triggers use the timezone stored in WORKFLOW_TIMEZONE. Do not hardcode UTC offsets. The weekly run day and time are stored as WORKFLOW_SCHEDULE_DAY ('Monday') and WORKFLOW_SCHEDULE_TIME ('07:00').
Credential store: full key inventory for the Competitor Monitoring automation
// Credential store contents
// All values are encrypted at rest. Reference by key name only in workflow steps.
SEMRUSH_API_KEY = "<semrush_account_api_key>"
SEMRUSH_DATABASE = "us" // e.g. us, uk, au
AHREFS_API_TOKEN = "<ahrefs_bearer_token>"
GOOGLE_ALERTS_FEEDS = [ // JSON array
{ "competitor_name": "CompetitorA", "feed_url": "https://www.google.com/alerts/feeds/<uid>/<alert_id>" },
{ "competitor_name": "CompetitorB", "feed_url": "https://www.google.com/alerts/feeds/<uid>/<alert_id>" }
// ... one entry per tracked competitor
]
GOOGLE_SHEETS_CREDENTIAL = "<service_account_json_key_or_oauth_refresh_token>"
GOOGLE_SHEETS_SPREADSHEET_ID = "<google_sheets_file_id>"
SLACK_BOT_TOKEN = "xoxb-<slack_bot_token>"
SLACK_CHANNEL_ID = "<slack_channel_id>" // e.g. C01234ABCDE
HUBSPOT_PRIVATE_APP_TOKEN = "<hubspot_private_app_token>"
HUBSPOT_COMPETITOR_RECORD_MAP = { // JSON object
"CompetitorA": "<hubspot_company_id>",
"CompetitorB": "<hubspot_company_id>"
// ... one entry per tracked competitor
}
AI_SUMMARISER_API_KEY = "<llm_provider_api_key>" // e.g. OpenAI or equivalent
AI_SUMMARISER_MODEL = "gpt-4o" // or configured model name
AI_SUMMARISER_PROMPT_VERSION = "v1.2" // increment on prompt changes
WORKFLOW_TIMEZONE = "America/New_York"
WORKFLOW_SCHEDULE_DAY = "Monday"
WORKFLOW_SCHEDULE_TIME = "07:00"
// Do not hardcode any of the above values in workflow step configurations.
// Rotate tokens in the credential store only; update both agents if a shared token changes.05Error handling and retry logic
Every integration point in the Competitor Monitoring automation has a defined failure behaviour. Unhandled exceptions must never fail silently. If a step fails and the defined retry and fallback logic cannot recover, the run must be marked failed, an alert must be posted to the Slack ops channel (SLACK_OPS_CHANNEL_ID), and a log entry must be written before execution halts.
All retry sequences use exponential backoff: first retry after 30 s, second after 2 min, third after 10 min. After three failed attempts, escalate per the behaviour defined in the table below. Do not retry indefinitely.
Integration
Scenario
Required behaviour
SEMrush API
429 Too Many Requests (rate limit hit)
Pause execution for 60 s, then retry up to 3 times with exponential backoff. If all retries fail, mark the SEMrush step as partial-failure, continue the run with missing SEMrush data, and annotate the digest with 'SEMrush data unavailable this week'.
SEMrush API
401 Unauthorized (invalid or expired API key)
Halt the run immediately. Post an alert to SLACK_OPS_CHANNEL_ID: 'CRITICAL: SEMrush API key invalid. Run halted. Action required: rotate key in credential store.' Do not proceed to Ahrefs or subsequent steps.
Ahrefs API
403 Forbidden (plan does not include endpoint access)
Halt the Ahrefs step. Post alert to SLACK_OPS_CHANNEL_ID with endpoint details. Continue run without Ahrefs data, annotate digest with 'Ahrefs data unavailable: plan restriction'. Log the error with full response body.
Ahrefs API
500 / 503 Server Error (Ahrefs outage)
Retry 3 times with exponential backoff. If all retries fail, continue run without Ahrefs data. Annotate digest with 'Ahrefs data unavailable: upstream error'. Post warning to SLACK_OPS_CHANNEL_ID.
Google Alerts RSS
Feed returns HTTP 404 (alert deleted or URL changed)
Log the missing feed URL and competitor name. Skip that competitor's alert data for this run. Do not halt the run. Post a warning to SLACK_OPS_CHANNEL_ID: 'Google Alert feed not found for [competitor_name]. Update GOOGLE_ALERTS_FEEDS in credential store.'
Google Alerts RSS
Feed returns zero items (no mentions this week)
Treat as a valid empty result. Pass an empty array to the staged dataset for that competitor's alerts field. Do not raise an error. The AI summariser should note 'No new mentions this week' for that competitor.
Google Sheets (read)
Competitor list tab not found or returns 0 rows
Halt the entire run immediately. Do not proceed to data collection. Post alert to SLACK_OPS_CHANNEL_ID: 'CRITICAL: Competitor list empty or sheet not found. Run aborted. Check GOOGLE_SHEETS_SPREADSHEET_ID and tab name.'
Google Sheets (write)
Append request fails with 403 or 429
Retry 3 times with exponential backoff. If all retries fail, save the row payload to the execution log in JSON format so it can be manually pasted into the sheet. Post warning to SLACK_OPS_CHANNEL_ID. Do not halt the Slack or HubSpot steps.
AI Summariser (LLM)
API timeout or 500 error from LLM provider
Retry once after 30 s. If the second attempt fails, post the raw structured data to SLACK_OPS_CHANNEL_ID as a JSON attachment with the message 'AI summariser unavailable: raw data attached for manual digest'. Halt Slack digest post but still append raw data to Google Sheets.
Slack (chat.postMessage)
channel_not_found or not_in_channel error
Halt the Slack step. Do not retry. Post an alert to SLACK_OPS_CHANNEL_ID (if different from the target channel). Log the full digest text to the execution log so it can be recovered. Email support@gofullspec.com if the ops channel is also unreachable.
HubSpot Notes API
Competitor not found in HUBSPOT_COMPETITOR_RECORD_MAP
Skip the note for that competitor. Log a warning in the execution log: 'No HubSpot record ID for [competitor_name]. Update HUBSPOT_COMPETITOR_RECORD_MAP in credential store.' Do not halt the run or affect other competitors' notes.
HubSpot Notes API
401 Unauthorized (expired private app token)
Halt the HubSpot step for all competitors in this run. Post alert to SLACK_OPS_CHANNEL_ID: 'CRITICAL: HubSpot token invalid. Rotate HUBSPOT_PRIVATE_APP_TOKEN in credential store.' All previous steps (Sheets, Slack) are unaffected and their results are preserved.
Any error not matched by the scenarios above is an unhandled exception. Unhandled exceptions must never fail silently. The orchestration layer must catch all unhandled exceptions, write a full error log entry including the step name, error code, response body, and run timestamp, and post an alert to SLACK_OPS_CHANNEL_ID before terminating the run. Contact support@gofullspec.com if unhandled exceptions recur across multiple weekly runs.
Integration and API SpecPage 3 of 3