FS-DOC-05Technical
Integration and API Spec
New Initiative Scoping and Approval
[YourCompany.com] · Management Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for every integration point in the New Initiative Scoping and Approval automation. It covers tool authentication, required OAuth scopes, webhook and trigger configuration, field mappings between agents, the credential store layout, and defined failure behaviour for every integration. The FullSpec team uses this specification to build, configure, and validate all connections. Your team's responsibility is to provide valid credentials and API access for each tool listed below before build begins.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Google Forms
Structured initiative intake trigger and reviewer assessment form
OAuth 2.0 (Google Workspace)
Free (Google account)
Agent 1 (trigger), Agent 2 (reviewer form)
Notion
Scoping page creation, status updates, and leadership review records
OAuth 2.0 (Notion integration token)
Plus ($16/month)
Agent 2, Agent 3
Google Sheets
Central initiative pipeline tracker; read/write for all status changes
OAuth 2.0 (Google Workspace)
Free (Google account)
Agent 2, Agent 3
Slack
Proposer gap notifications and reviewer routing messages
OAuth 2.0 (Bot token, Slack app)
Pro ($8/month)
Agent 1, Agent 2
Gmail
Daily leadership approval digest delivery
OAuth 2.0 (Google Workspace)
Free (Google account)
Agent 3
Automation platform
Workflow orchestration, credential store, scheduling, and retry logic
Platform-managed (internal)
Paid tier ($49/month)
All agents
Before you connect anything: every integration must be tested against a sandbox or development environment before production credentials are entered. For Google Workspace tools, use a dedicated test Google account. For Notion, use a duplicate workspace. For Slack, use a private test channel. Do not point any agent at live data until end-to-end testing is complete and signed off.
02Per-tool integration detail
Google Forms
Google Forms acts as the intake trigger (Agent 1) and as the reviewer assessment submission point (Agent 2). Two separate forms are required: one for proposer intake and one for reviewer structured feedback. Both are connected via the Google Forms API through the orchestration layer.
Auth method
OAuth 2.0 via Google Workspace. A service account with domain-wide delegation is preferred so that the automation platform can read responses without an interactive login session.
Required scopes
https://www.googleapis.com/auth/forms.responses.readonly | https://www.googleapis.com/auth/forms.body.readonly | https://www.googleapis.com/auth/drive.readonly (required to resolve form IDs from Drive)
Webhook / trigger setup
Google Forms does not natively support outbound webhooks. The orchestration layer must poll the Forms API endpoint GET /v1/forms/{formId}/responses?filter=timestamp+gt+{last_checked_timestamp} on a 5-minute interval. The last-checked timestamp is persisted in the credential store between polls. Alternatively, a Google Apps Script trigger on the form can POST to an orchestration-layer webhook URL on each new response, which reduces latency to under 30 seconds. The Apps Script URL must be stored in the credential store.
Required configuration
Two form IDs must be stored in the credential store: INTAKE_FORM_ID and REVIEWER_FORM_ID. Neither ID is hardcoded in workflow logic. The intake form must contain fields matching the required field checklist (see Section 03). The reviewer form must be linked from each Notion scoping page by the Scoping and Routing Agent at page creation time.
Rate limits
Google Forms API: 300 requests/minute per project. At current volume (8-15 submissions/month), polling every 5 minutes produces fewer than 300 daily API calls. No throttling is required at this volume.
Constraints
Form response objects do not include the form question text, only question IDs. A static mapping of questionId to field name must be maintained in the credential store and updated if the form is edited. Editing the intake form after go-live requires a coordinated update to this mapping.
// Input
GET /v1/forms/{INTAKE_FORM_ID}/responses
param: filter = timestamp gt {last_poll_timestamp}
// Output
responses[].answers[questionId].textAnswers.answers[0].value
responses[].responseId -> used as external reference keyNotion
Notion stores the structured scoping record for each initiative and is updated at two points: when the Scoping and Routing Agent creates the page, and when the reviewer outcome is written back. A locked page template in a dedicated Notion database is the source of structure for all scoping records.
Auth method
Notion internal integration token (Bearer token). The integration must be manually connected to the target database in Notion workspace settings. OAuth 2.0 public integration is not required at this stage.
Required scopes
Notion integration capabilities (set at integration creation): Read content, Update content, Insert content. The integration must be granted explicit access to: the Scoping Records database, the locked page template, and any linked databases used for reviewer assignments.
Webhook / trigger setup
Notion does not provide outbound webhooks. The orchestration layer polls the database using POST /v1/databases/{NOTION_DB_ID}/query with a filter on last_edited_time to detect status changes. Poll interval: 10 minutes. The Notion database ID is stored in the credential store as NOTION_SCOPING_DB_ID.
Required configuration
NOTION_SCOPING_DB_ID stored in credential store. NOTION_TEMPLATE_PAGE_ID stored in credential store (the locked scoping template page ID). Page properties must include: Initiative_Title (title), Reference_ID (rich_text), Status (select: Pending Review, Under Review, Approved, Declined, Revision Requested), Reviewer_Name (rich_text), Review_Deadline (date), Budget_Estimate (number), Reviewer_Recommendation (select: Approve, Revise, Decline), Leadership_Decision (select: Approved, Declined, Hold). All property names are case-sensitive and must not be renamed after go-live.
Rate limits
Notion API: 3 requests/second average, burst to 90 requests/minute. At current volume each agent run creates or updates 1-2 pages. No throttling required. If batch operations are needed, space requests 350ms apart.
Constraints
Page content (body blocks) cannot be updated via the API in the same call as page properties. Two separate API calls are required: PATCH /v1/pages/{page_id} for properties, and PATCH /v1/blocks/{block_id}/children for body content. Template duplication via the API duplicates the page structure but does not preserve locked block settings; the automation must set all field values explicitly rather than relying on template defaults.
// Create page (Agent 2)
POST /v1/pages
parent.database_id = NOTION_SCOPING_DB_ID
properties: { Initiative_Title, Reference_ID, Status='Pending Review',
Reviewer_Name, Review_Deadline, Budget_Estimate }
// Update page with review outcome (Agent 2, webhook poll)
PATCH /v1/pages/{page_id}
properties: { Status, Reviewer_Recommendation }
// Output
page.id -> stored as NOTION_PAGE_ID in run context
page.url -> included in Slack notification to reviewerGoogle Sheets
Google Sheets acts as the central initiative pipeline tracker. Agent 2 writes a new row on each validated submission. Agent 3 reads the tracker daily to identify initiatives awaiting a leadership decision and writes the final decision status once recorded in Notion.
Auth method
OAuth 2.0 via Google Workspace service account with domain-wide delegation. The same service account used for Google Forms can be reused if scopes are extended.
Required scopes
https://www.googleapis.com/auth/spreadsheets | https://www.googleapis.com/auth/drive.readonly (to resolve the spreadsheet ID from Drive if referenced by name)
Webhook / trigger setup
No webhook. Agent 3 reads the tracker on a scheduled poll: Monday to Friday at 07:50 AM (10 minutes before digest send time) using GET /v4/spreadsheets/{SHEETS_TRACKER_ID}/values/{TRACKER_SHEET_NAME}!A:K. The schedule is configured in the orchestration layer.
Required configuration
SHEETS_TRACKER_ID stored in credential store. TRACKER_SHEET_NAME stored in credential store (default: 'Initiative Tracker'). Column structure must be fixed and match the field mapping table in Section 03. Columns must not be reordered after go-live. Headers must appear in row 1; data begins at row 2.
Rate limits
Sheets API: 300 requests/minute per project, 60 requests/minute per user. At current volume (8-15 rows/month, 1 daily read by Agent 3) the total monthly API calls are under 200. No throttling required.
Constraints
Appending rows uses the values.append method, which is sensitive to the detected table range. If a human manually adds rows below the data range, the append target may shift. The automation must always append to a named range (TRACKER_DATA_RANGE) rather than a dynamic range. Conditional formatting and protected ranges set by a human editor do not affect API write operations but may confuse manual reviewers of the sheet.
// Append new row (Agent 2)
POST /v4/spreadsheets/{SHEETS_TRACKER_ID}/values/{TRACKER_SHEET_NAME}!A:K:append
values: [ [Reference_ID, Proposer_Name, Submission_Date,
Initiative_Title, Category, Budget_Estimate,
Reviewer_Name, Review_Deadline, Status,
Notion_Page_URL, Leadership_Decision] ]
// Read pending rows (Agent 3)
GET /v4/spreadsheets/{SHEETS_TRACKER_ID}/values/{TRACKER_SHEET_NAME}!A:K
filter client-side: Status IN ('Under Review', 'Revision Requested')
and Reviewer_Recommendation IN ('Approve', 'Revise')Integration and API SpecPage 1 of 3
FS-DOC-05Technical
Slack
Slack is used by Agent 1 to notify the proposer of any incomplete fields in their submission, and by Agent 2 to notify the assigned reviewer with a link to the Notion scoping page and the review deadline. A Slack app with a bot token is required. The bot must be invited to all channels used for notifications.
Auth method
OAuth 2.0 Slack app with Bot token (xoxb-). The bot is created in the Slack App management console at api.slack.com/apps. The bot token is stored in the credential store as SLACK_BOT_TOKEN. No user token is required.
Required scopes
chat:write | chat:write.public (if posting to channels the bot has not been invited to) | users:read | users:read.email (to resolve reviewer Slack user IDs from email addresses stored in the routing table) | channels:read (to validate channel existence before posting)
Webhook / trigger setup
Slack is output-only in this automation. No incoming webhooks or event subscriptions are required. All messages are sent via POST to https://slack.com/api/chat.postMessage with the bot token. Direct messages to a user require resolving the user ID via users.lookupByEmail before posting.
Required configuration
SLACK_BOT_TOKEN in credential store. SLACK_PROPOSER_FALLBACK_CHANNEL in credential store (a fallback channel for cases where the proposer's Slack user ID cannot be resolved from their email). Reviewer Slack user IDs are resolved dynamically at runtime from the reviewer email address stored in the routing rules table. The routing rules table is stored in the credential store as a JSON object (REVIEWER_ROUTING_TABLE) keyed by initiative category.
Rate limits
Slack API: Tier 3 methods (chat.postMessage) allow 50 requests/minute. At current volume (8-15 messages/month to proposers plus 8-15 to reviewers) the monthly call count is under 50. No throttling required.
Constraints
Slack user IDs must be resolved before sending direct messages. If a proposer or reviewer does not have a Slack account matching their form submission email, the agent must fall back to posting in the SLACK_PROPOSER_FALLBACK_CHANNEL with the user's name included in the message body. Message blocks using Block Kit are preferred over plain text for reviewer notifications to ensure the Notion link and deadline are visually distinct.
// Agent 1: gap notification to proposer
POST https://slack.com/api/chat.postMessage
token: SLACK_BOT_TOKEN
channel: {proposer_slack_user_id} OR SLACK_PROPOSER_FALLBACK_CHANNEL
blocks: [ section(missing_fields_list), section(resubmit_link) ]
// Agent 2: reviewer notification
POST https://slack.com/api/chat.postMessage
token: SLACK_BOT_TOKEN
channel: {reviewer_slack_user_id}
blocks: [ section(initiative_title, reference_id),
section(notion_page_url), section(review_deadline) ]Gmail
Gmail is used exclusively by Agent 3 to send the daily leadership approval digest. The digest is a formatted HTML email sent to the founder or CEO each weekday morning at 08:00 AM if at least one initiative is pending a leadership decision. The automation sends via the Gmail API using a designated sender account.
Auth method
OAuth 2.0 via Google Workspace. A dedicated sender account (e.g. initiatives@[YourCompany.com]) is strongly recommended rather than using the CEO's personal account. The OAuth refresh token for this account is stored in the credential store as GMAIL_SENDER_REFRESH_TOKEN.
Required scopes
https://www.googleapis.com/auth/gmail.send (send-only; do not request broader gmail.modify or mail.google.com scopes)
Webhook / trigger setup
Gmail is output-only. No webhook or trigger setup is required on the Gmail side. Agent 3 is triggered by the orchestration layer scheduler at 07:50 AM Monday to Friday. If no initiatives meet the send condition, the Gmail API is not called and no email is sent.
Required configuration
GMAIL_SENDER_ADDRESS in credential store. GMAIL_DIGEST_RECIPIENT in credential store (the founder or CEO email address). GMAIL_SENDER_REFRESH_TOKEN in credential store. The digest HTML template is stored in the orchestration layer as a template string with placeholders: {{initiative_rows}}, {{total_budget_pending}}, {{digest_date}}. The template is not hardcoded inline; it is stored as GMAIL_DIGEST_TEMPLATE in the credential store.
Rate limits
Gmail API: 250 quota units/second per user; sending a message costs 100 units. At one email per weekday the monthly send count is approximately 22. No throttling required.
Constraints
Gmail API send requires the message to be MIME-encoded and base64url-encoded before submission. The orchestration layer must handle this encoding. The From header must match the authenticated sender account exactly or the send will be rejected. Attachments are not used in this automation; the digest is inline HTML only.
// Agent 3: send digest
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
Authorization: Bearer {access_token from GMAIL_SENDER_REFRESH_TOKEN}
body.raw: base64url({
From: GMAIL_SENDER_ADDRESS,
To: GMAIL_DIGEST_RECIPIENT,
Subject: 'Initiative Decisions Pending - {digest_date}',
Content-Type: text/html; charset=utf-8,
body: rendered(GMAIL_DIGEST_TEMPLATE, { initiative_rows,
total_budget_pending, digest_date })
})03Field mappings between tools
The tables below document every field handoff between tools at each agent boundary. Use exact field names as shown; these are the names configured in the form, spreadsheet, and Notion database. Any rename in a source tool requires a corresponding update to the orchestration layer mapping.
Agent 1 handoff: Google Forms intake response to Intake Validation Agent (validation check inputs)
Source tool
Source field
Destination tool
Destination field
Google Forms
proposer_name
Validation logic
proposer_name
Google Forms
initiative_title
Validation logic
initiative_title
Google Forms
strategic_rationale
Validation logic
strategic_rationale
Google Forms
estimated_effort_days
Validation logic
estimated_effort_days
Google Forms
estimated_budget_usd
Validation logic
estimated_budget_usd
Google Forms
affected_departments
Validation logic
affected_departments
Google Forms
initiative_category
Validation logic
initiative_category
Google Forms
proposer_email
Validation logic
proposer_email
Google Forms
responseId
Run context
external_reference_key
Agent 1 handoff (failure path): Intake Validation Agent to Slack (gap notification to proposer)
Source tool
Source field
Destination tool
Destination field
Validation logic
proposer_email
Slack (users.lookupByEmail)
user.id
Validation logic
missing_fields_list[]
Slack (chat.postMessage)
blocks[].text
Validation logic
initiative_title
Slack (chat.postMessage)
blocks[0].text (subject line)
Agent 2 handoff: validated form data to Notion scoping page creation
Source tool
Source field
Destination tool
Destination field
Google Forms
initiative_title
Notion
Initiative_Title (title property)
Google Forms
proposer_name
Notion
Proposer_Name (rich_text property)
Google Forms
proposer_email
Notion
Proposer_Email (email property)
Google Forms
strategic_rationale
Notion
Strategic_Rationale (rich_text block)
Google Forms
estimated_effort_days
Notion
Estimated_Effort_Days (number property)
Google Forms
estimated_budget_usd
Notion
Budget_Estimate (number property)
Google Forms
affected_departments
Notion
Affected_Departments (multi_select property)
Google Forms
initiative_category
Notion
Initiative_Category (select property)
Run context
generated_reference_id
Notion
Reference_ID (rich_text property)
Run context
review_deadline (calculated)
Notion
Review_Deadline (date property)
REVIEWER_ROUTING_TABLE
reviewer_name (looked up by initiative_category)
Notion
Reviewer_Name (rich_text property)
Agent 2 handoff: Notion page creation output to Google Sheets tracker row append
Source tool
Source field
Destination tool
Destination field
Run context
generated_reference_id
Google Sheets
Reference_ID (col A)
Google Forms
proposer_name
Google Sheets
Proposer_Name (col B)
Run context
submission_timestamp
Google Sheets
Submission_Date (col C)
Google Forms
initiative_title
Google Sheets
Initiative_Title (col D)
Google Forms
initiative_category
Google Sheets
Category (col E)
Google Forms
estimated_budget_usd
Google Sheets
Budget_Estimate (col F)
REVIEWER_ROUTING_TABLE
reviewer_name
Google Sheets
Reviewer_Name (col G)
Run context
review_deadline
Google Sheets
Review_Deadline (col H)
Hardcoded constant
'Pending Review'
Google Sheets
Status (col I)
Notion API response
page.url
Google Sheets
Notion_Page_URL (col J)
Hardcoded constant
''
Google Sheets
Leadership_Decision (col K)
Agent 2 handoff: Google Sheets tracker to Slack reviewer notification
Source tool
Source field
Destination tool
Destination field
REVIEWER_ROUTING_TABLE
reviewer_email
Slack (users.lookupByEmail)
user.id
Google Sheets
Initiative_Title (col D)
Slack (chat.postMessage)
blocks[0].text
Google Sheets
Reference_ID (col A)
Slack (chat.postMessage)
blocks[1].text
Google Sheets
Notion_Page_URL (col J)
Slack (chat.postMessage)
blocks[2].accessory.url
Google Sheets
Review_Deadline (col H)
Slack (chat.postMessage)
blocks[3].text
Agent 3 handoff: Google Sheets tracker read to Gmail digest composition
Source tool
Source field
Destination tool
Destination field
Google Sheets
Initiative_Title (col D)
Gmail (digest template)
{{initiative_rows}}[n].title
Google Sheets
Reviewer_Name (col G)
Gmail (digest template)
{{initiative_rows}}[n].reviewer
Google Sheets
Notion_Page_URL (col J)
Gmail (digest template)
{{initiative_rows}}[n].link
Google Sheets
Budget_Estimate (col F)
Gmail (digest template)
{{initiative_rows}}[n].budget
Google Sheets
Status (col I)
Gmail (digest template)
{{initiative_rows}}[n].status
Aggregation (SUM col F)
total_budget_pending
Gmail (digest template)
{{total_budget_pending}}
Run context
digest_date
Gmail (digest template)
{{digest_date}}
Integration and API SpecPage 2 of 3
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Three discrete workflows are configured in the automation platform, one per agent. Workflows share a single credential store. No workflow calls another workflow directly; Agent 2 is triggered by a status flag written to a shared run-context variable by Agent 1, and Agent 3 is triggered by the platform scheduler independently of Agents 1 and 2.
Agent 1 trigger mechanism
Poll. The orchestration layer polls the Google Forms API every 5 minutes for new intake form responses, filtering by timestamp greater than the last processed response timestamp. Optionally, a Google Apps Script onFormSubmit trigger can POST to an orchestration-layer inbound webhook URL to reduce latency to under 60 seconds. If the webhook approach is used, Slack signature validation is not applicable here (this is a Google-originated POST); the orchestration layer must validate that the inbound request contains a shared secret header (FORMS_WEBHOOK_SECRET) before processing.
Agent 2 trigger mechanism
Event-based (internal). Agent 2 fires when Agent 1 writes a validated submission object to the shared run context with status = 'validated'. The orchestration platform evaluates this condition after every Agent 1 execution. No external webhook is involved.
Agent 3 trigger mechanism
Scheduled poll. The orchestration layer fires Agent 3 Monday to Friday at 07:50 AM local business timezone (stored in the platform timezone setting as DIGEST_TIMEZONE). Agent 3 reads the Google Sheets tracker, applies the pending-decision filter, and proceeds to Gmail send only if at least one qualifying row exists. If the tracker returns zero qualifying rows, the workflow exits without calling the Gmail API.
Credential store type
The automation platform's native encrypted secret store. All secrets are referenced by name (environment variable style) and never interpolated into logs or error messages. Rotation of any secret requires updating only the credential store entry; no workflow code changes are needed.
Reference ID generation
The unique initiative reference ID is generated by Agent 2 at the moment of Notion page creation. Format: INI-{YYYYMMDD}-{4-digit-zero-padded-sequence}. The sequence counter is stored in the credential store as INITIATIVE_SEQUENCE_COUNTER and incremented atomically by Agent 2 on each successful Notion page creation.
Credential store contents (all entries below must be populated before any workflow is activated):
Credential store entries: all values are secrets and must never appear in workflow logs, error messages, or notification text
# Google Workspace (shared service account)
GOOGLE_SERVICE_ACCOUNT_JSON = { ... } # full service account key JSON
GOOGLE_DELEGATED_USER_EMAIL = 'automation@[YourCompany.com]'
# Google Forms
INTAKE_FORM_ID = 'string' # Google Form ID for proposer intake
REVIEWER_FORM_ID = 'string' # Google Form ID for reviewer assessment
FORMS_LAST_POLL_TIMESTAMP = 'ISO8601' # updated after each successful poll
FORMS_WEBHOOK_SECRET = 'string' # shared secret if Apps Script webhook used
# Notion
NOTION_INTEGRATION_TOKEN = 'secret_xxxxxxxxxxxxxxxxxxxx'
NOTION_SCOPING_DB_ID = 'string' # Notion database ID for scoping records
NOTION_TEMPLATE_PAGE_ID = 'string' # ID of the locked scoping page template
# Google Sheets
SHEETS_TRACKER_ID = 'string' # Spreadsheet ID
TRACKER_SHEET_NAME = 'Initiative Tracker'
TRACKER_DATA_RANGE = 'Initiative Tracker!A:K'
# Slack
SLACK_BOT_TOKEN = 'xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx'
SLACK_PROPOSER_FALLBACK_CHANNEL = '#initiative-submissions'
# Gmail
GMAIL_SENDER_ADDRESS = 'initiatives@[YourCompany.com]'
GMAIL_DIGEST_RECIPIENT = 'founder@[YourCompany.com]'
GMAIL_SENDER_REFRESH_TOKEN = 'string' # OAuth refresh token for sender account
GMAIL_DIGEST_TEMPLATE = 'string' # HTML template with {{placeholders}}
# Routing and sequencing
REVIEWER_ROUTING_TABLE = '{"Product": {"name": "...", "email": "..."},
"Operations": {"name": "...", "email": "..."},
"Finance": {"name": "...", "email": "..."}}'
INITIATIVE_SEQUENCE_COUNTER = 1 # integer, incremented atomically
DIGEST_TIMEZONE = 'America/New_York' # adjust to business timezone
# Validation thresholds
MIN_WORD_COUNT_STRATEGIC_RATIONALE = 30 # minimum words required in rationale field
MIN_WORD_COUNT_INITIATIVE_TITLE = 3 # minimum words required in title field05Error handling and retry logic
Unhandled exceptions must never fail silently. Every workflow must write a structured error log entry to the platform's execution log on any uncaught exception, including the workflow name, the step that failed, the error code or message, and a timestamp. The FullSpec team configures a platform-level alert to notify support@gofullspec.com and the designated process owner email when any workflow enters a failed state.
Integration
Scenario
Required behaviour
Google Forms (poll)
Poll returns HTTP 429 Too Many Requests
Retry after 60 seconds with exponential backoff (60s, 120s, 240s). After 3 failed retries, log the error and skip the poll cycle. Resume on the next scheduled poll interval. Alert the process owner if 3 consecutive poll cycles fail.
Google Forms (poll)
Response payload is malformed or missing expected question IDs
Log the raw response and the mismatched question IDs. Do not pass the response to Agent 1 validation logic. Send a Slack alert to SLACK_PROPOSER_FALLBACK_CHANNEL: 'Intake form response could not be parsed. Manual review required. Response ID: {responseId}.'
Slack (Agent 1 gap notification)
users.lookupByEmail returns no match for proposer email
Fall back to posting the gap notification in SLACK_PROPOSER_FALLBACK_CHANNEL with the proposer name and email included in the message body. Do not abort the workflow. Log the fallback event.
Slack (Agent 2 reviewer notification)
chat.postMessage returns HTTP 404 (channel not found or user not in workspace)
Retry once after 30 seconds. If the second attempt fails, log the error and send a fallback email to the reviewer address from REVIEWER_ROUTING_TABLE using the Gmail sender account. Do not allow the reviewer notification to be silently dropped.
Notion (page creation)
API returns HTTP 400 (validation error on a property value)
Log the full request body and the Notion error response. Do not retry automatically. Send a Slack alert to SLACK_PROPOSER_FALLBACK_CHANNEL: 'Notion page creation failed for initiative {initiative_title}. Manual creation required.' Abort the Agent 2 run for this submission.
Notion (page creation)
API returns HTTP 503 or connection timeout
Retry up to 3 times with exponential backoff (30s, 90s, 270s). If all retries fail, log the error and queue the submission in the credential store under FAILED_NOTION_QUEUE for manual reprocessing. Alert the process owner.
Google Sheets (row append)
API returns HTTP 400 (range not found or sheet name mismatch)
Do not retry. Log the error including the target range value from TRACKER_DATA_RANGE. Alert the process owner immediately: the sheet structure may have been modified. The Notion page has already been created; the tracker row must be added manually to keep records consistent.
Google Sheets (Agent 3 read)
Spreadsheet returns empty result set (no data in range)
Treat as a valid zero-result state. Do not send the Gmail digest. Log 'No pending initiatives found at {timestamp}. Digest not sent.' Exit the workflow cleanly with a success status.
Gmail (digest send)
OAuth access token has expired and refresh fails
Attempt token refresh using GMAIL_SENDER_REFRESH_TOKEN. If the refresh request itself fails (e.g. token revoked), log the error and send a Slack alert to SLACK_PROPOSER_FALLBACK_CHANNEL: 'Leadership digest could not be sent. Gmail token requires reauthorisation.' Do not retry the send until the token is refreshed. Alert the process owner.
Gmail (digest send)
API returns HTTP 400 (invalid recipient address in GMAIL_DIGEST_RECIPIENT)
Do not retry. Log the invalid address and abort the send. Alert the process owner via Slack. This indicates a misconfiguration in the credential store that requires manual correction before the next scheduled digest.
REVIEWER_ROUTING_TABLE
initiative_category from the intake form does not match any key in the routing table
Do not proceed with reviewer assignment. Log the unmatched category value. Send a Slack alert to SLACK_PROPOSER_FALLBACK_CHANNEL: 'Reviewer routing failed for category "{category}". Assign a reviewer manually and update the routing table.' Set the Google Sheets Status to 'Routing Failed' for visibility.
All integrations
Uncaught exception at any workflow step
Write a structured error record to the platform execution log: { workflow, step, error_code, error_message, timestamp, run_id }. Halt the current run. Trigger a platform-level alert to support@gofullspec.com and the process owner email. Never suppress or swallow an exception without logging it.
Integration and API SpecPage 3 of 3