FS-DOC-05Technical
Integration and API Spec
Insurance Renewal Management
[YourCompany.com] · Legal Department · Prepared by FullSpec · [Today's Date]
This document specifies every integration point, API configuration, field mapping, and error-handling behaviour required to build and operate the Insurance Renewal Management automation. It is written for the FullSpec build team and covers all six connected tools across three agents: Renewal Tracker Agent, Quote Comparison Agent, and Approval and Completion Agent. Nothing in this document should be treated as final until sandbox connections have been verified and the credential store is fully populated.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
Automation platform (orchestration layer)
Hosts all three agent workflows, credential store, retry logic, and scheduling
Internal service account
N/A (build choice)
All agents
Notion
Central policy register: expiry dates, broker contacts, coverage records, renewal status
OAuth 2.0 / Internal Integration Token
Plus ($16/month)
Agents 1, 2, 3
Google Workspace (Gmail + Drive)
Broker email outreach, quote document intake, acceptance notifications, document filing
OAuth 2.0
Business Starter or above
Agents 1, 3
HubSpot
Broker outreach logging, chase timer tracking, contact record updates
OAuth 2.0 / Private App token
Starter CRM (free tier insufficient for API write access at scale)
Agent 1
Slack
Internal alerts: expiry warnings, comparison summaries, exception flags
OAuth 2.0 / Bot token
Pro or above (webhook posting works on free but channel history limits apply)
Agents 1, 2
DocuSign
Structured approval envelope routing, signed approval capture, audit trail
OAuth 2.0 (JWT Grant)
Standard or above
Agent 3
Xero
Draft bill creation against correct supplier and account code on approval
OAuth 2.0
Starter or above
Agent 3
Before you connect anything: create sandbox or developer accounts for every tool in the inventory and complete all connection tests against those environments before any production credentials are entered. Notion integration tokens, HubSpot private app tokens, DocuSign JWT keys, and Xero OAuth clients must all be provisioned separately for sandbox and production. Do not reuse sandbox tokens in the live credential store.
02Per-tool integration detail
Notion
Notion serves as the system of record for all policy data. The automation reads expiry dates on a daily schedule, writes renewal status updates throughout the workflow, and performs a final record update when the Approval and Completion Agent closes a renewal cycle.
Auth method
Notion Internal Integration Token (server-to-server). Token is scoped to a single shared workspace integration. OAuth 2.0 user-delegated flow is not required for this use case.
Required scopes
Read content (pages, databases): read_content. Update content: update_content. Insert content: insert_content. No user or workspace admin scopes are required.
Trigger setup
Notion does not support outbound webhooks natively. The orchestration layer must poll the policy register database using a scheduled GET /v1/databases/{database_id}/query call on a daily interval (recommended: 06:00 UTC). Filter applied: filter.property = 'Expiry Date', filter.date.on_or_before = today + 90 days. The database ID must be stored in the credential store, not hardcoded.
Required configuration
Policy register database must contain the following properties: Policy Name (title), Expiry Date (date), Broker Name (text), Broker Email (email), Prior Premium (number), Policy Number (text), Coverage Type (select), Renewal Status (select: Active / In Progress / Renewed / Lapsed), Last Broker Contact (date), Next Renewal Date (date), Xero Bill ID (text), DocuSign Envelope ID (text). Database ID stored in credential store as NOTION_POLICY_DB_ID.
Rate limits
Notion API enforces a limit of 3 requests/second per integration token. At a volume of 6 to 20 policies/year with daily polling, peak load is well under 10 requests/day. No throttling wrapper is required at current volume, but exponential backoff must be implemented for 429 responses.
Constraints
Notion API does not support file attachment upload via API. Policy documents must be filed in Google Drive and the Drive URL linked as a text property in Notion. Queries are limited to 100 results per page; pagination must be handled if the database exceeds 100 records.
// Poll query (daily)
GET /v1/databases/{NOTION_POLICY_DB_ID}/query
Body: { filter: { property: 'Expiry Date', date: { on_or_before: '<today+90d>' } } }
// Update record on renewal completion
PATCH /v1/pages/{page_id}
Body: { properties: { 'Renewal Status': 'Renewed', 'Next Renewal Date': '<date>', 'Xero Bill ID': '<id>', 'DocuSign Envelope ID': '<id>' } }Google Workspace (Gmail + Drive)
Gmail handles all outbound broker communication and inbound quote intake. Google Drive stores policy documents and quote files, with Drive file URLs referenced back to the Notion policy record. Two distinct Gmail operations are required: sending from a designated service address and reading an intake mailbox for incoming broker quotes.
Auth method
OAuth 2.0 with a dedicated service account. The service account must have domain-wide delegation enabled in Google Workspace Admin to send mail on behalf of the designated renewal inbox (e.g. renewals@[YourCompany.com]). Client credentials stored as GMAIL_SERVICE_ACCOUNT_KEY_JSON and GMAIL_DELEGATE_ADDRESS in the credential store.
Required scopes
Send email: https://www.googleapis.com/auth/gmail.send. Read email and attachments: https://www.googleapis.com/auth/gmail.readonly. Modify labels: https://www.googleapis.com/auth/gmail.modify. Create and write files in Drive: https://www.googleapis.com/auth/drive.file. Read files in designated Drive folders: https://www.googleapis.com/auth/drive.readonly.
Webhook/trigger setup
Gmail push notifications via Google Cloud Pub/Sub: set up a watch on the intake mailbox using POST /gmail/v1/users/{userId}/watch with topicName pointing to a Pub/Sub topic subscribed by the orchestration layer. Watch expiry is 7 days; the automation must re-register the watch every 6 days. Alternatively, poll the intake inbox every 15 minutes for new messages with label INBOX and attachment MIME types application/pdf or application/vnd.openxmlformats-officedocument.wordprocessingml.document.
Required configuration
Designated intake mailbox address stored as GMAIL_INTAKE_ADDRESS. Broker quote intake folder ID in Drive stored as GDRIVE_QUOTE_FOLDER_ID. Broker acceptance email template stored as a Google Doc template with placeholders: {{policy_name}}, {{broker_name}}, {{expiry_date}}, {{acceptance_note}}. Template Doc ID stored as GDRIVE_ACCEPTANCE_TEMPLATE_ID. Broker outreach request template stored as GDRIVE_OUTREACH_TEMPLATE_ID.
Rate limits
Gmail API: 250 quota units/second per user, 1 billion units/day. Sending 20 broker emails/day is negligible. Drive API: 1,000 requests/100 seconds per user. No throttling needed at current volume. Implement 429 backoff regardless.
Constraints
Service account delegation requires a Google Workspace Business plan (not personal Gmail). The watch subscription requires a verified Google Cloud project with Pub/Sub API enabled. Drive files must be shared with the service account email or the shared drive must have service account membership.
// Send broker outreach email
POST /gmail/v1/users/{GMAIL_DELEGATE_ADDRESS}/messages/send
Body: RFC 2822 message, To: {broker_email}, Subject: 'Renewal Request: {policy_name} - Expiry {expiry_date}'
// Poll intake inbox for quote attachments
GET /gmail/v1/users/{GMAIL_INTAKE_ADDRESS}/messages?q=has:attachment+is:unread+label:inbox
Then: GET /gmail/v1/users/{userId}/messages/{messageId} to fetch attachment metadata
Then: GET /gmail/v1/users/{userId}/messages/{messageId}/attachments/{attachmentId}HubSpot
HubSpot tracks broker outreach activity and manages the automatic chase sequence. Each broker is treated as a Contact record. Outreach events are logged as Engagements. A custom property on the Contact records the last outreach date and current chase status, enabling the Renewal Tracker Agent to determine when a follow-up is due.
Auth method
HubSpot Private App token (recommended over OAuth for server-to-server). Token stored as HUBSPOT_PRIVATE_APP_TOKEN in credential store. Scopes are selected at Private App creation time.
Required scopes
crm.objects.contacts.read. crm.objects.contacts.write. crm.objects.deals.read. crm.objects.deals.write. timeline (for engagement logging). crm.schemas.contacts.read.
Webhook/trigger setup
No inbound webhook from HubSpot is required. The orchestration layer drives HubSpot via API writes only. The Renewal Tracker Agent writes an engagement log entry after each outreach event and reads the contact record to check the hs_last_contacted timestamp to determine whether a chase is due.
Required configuration
Broker contacts must exist as Contact records in HubSpot before launch. A custom contact property must be created: renewal_chase_status (enumeration: Not Started / Outreach Sent / Chase 1 Sent / Chase 2 Sent / Quote Received). A second custom property: last_renewal_outreach_date (date). Both property internal names stored as HUBSPOT_CHASE_STATUS_PROP and HUBSPOT_OUTREACH_DATE_PROP in credential store. HubSpot Portal ID stored as HUBSPOT_PORTAL_ID.
Rate limits
HubSpot enforces 110 requests/10 seconds and 40,000 requests/day for Private App tokens on paid plans. At current volume (6 to 20 policies/year), daily API calls will not exceed 200. No throttling wrapper required. Implement standard 429 retry with backoff.
Constraints
Free CRM tier does not support custom properties via API without a paid seat. Starter plan or above required for full API write access. The Private App token does not support OAuth refresh; if the token is rotated, the credential store must be updated manually and a re-test run against the sandbox.
// Log outreach engagement
POST /crm/v3/objects/contacts/{contact_id}/associations
POST /engagements/v1/engagements
Body: { engagement: { type: 'EMAIL', timestamp: <epoch_ms> }, metadata: { subject: 'Renewal outreach: {policy_name}', body: '<email body snippet>' } }
// Update chase status property
PATCH /crm/v3/objects/contacts/{contact_id}
Body: { properties: { renewal_chase_status: 'Chase 1 Sent', last_renewal_outreach_date: '<ISO date>' } }Integration and API SpecPage 1 of 4
FS-DOC-05Technical
Slack
Slack receives two categories of automated message: expiry and outreach status notifications from the Renewal Tracker Agent, and the structured quote comparison summary from the Quote Comparison Agent. Messages are posted to a designated compliance channel. Exception flags (material coverage changes) use Slack's Block Kit to make the alert visually distinct.
Auth method
Slack Bot token (xoxb-). Bot is installed into the workspace via a Slack App with the required OAuth scopes. Token stored as SLACK_BOT_TOKEN in credential store.
Required scopes
chat:write (post messages to channels the bot is a member of). files:write (upload comparison summary documents if needed). channels:read (verify the compliance channel ID at boot). incoming-webhook (optional fallback if bot token flow is unavailable).
Webhook/trigger setup
All Slack interactions are outbound only from the automation platform. No inbound Slack events or slash commands are required. The bot must be invited to the compliance channel before the first message can be posted. Channel ID stored as SLACK_COMPLIANCE_CHANNEL_ID in credential store. Exception flag messages use Block Kit JSON with a danger-coloured section and action buttons for visual clarity (buttons are informational only and do not trigger workflow callbacks in this build).
Required configuration
Slack App created in api.slack.com/apps with bot token scopes listed above. Bot display name: RenewalBot (or as agreed with [YourCompany.com]). Compliance channel ID stored as SLACK_COMPLIANCE_CHANNEL_ID. Workspace ID stored as SLACK_WORKSPACE_ID. Comparison summary message template as a Block Kit JSON template stored in the orchestration layer's config.
Rate limits
Slack API Tier 3: up to 50 requests/minute for chat.postMessage. At 6 to 20 policies/year, peak load is a handful of messages per day. No throttling required. Implement retry with 1-second minimum backoff on 429 responses, respecting the Retry-After header.
Constraints
Free Slack plan limits message history to 90 days. Pro plan or above recommended so that comparison summaries remain searchable for audit purposes. The bot must be a member of the channel; posting to a private channel requires an explicit invite.
// Post comparison summary
POST https://slack.com/api/chat.postMessage
Headers: Authorization: Bearer {SLACK_BOT_TOKEN}
Body: { channel: '{SLACK_COMPLIANCE_CHANNEL_ID}', blocks: [ { type: 'section', text: { type: 'mrkdwn', text: '*Renewal Summary: {policy_name}*\nPremium delta: {delta}%\nCoverage flags: {flags}' } } ] }
// Exception flag (material change detected)
Add block: { type: 'section', text: { type: 'mrkdwn', text: ':warning: *Material coverage change detected. Human review required before DocuSign is triggered.*' } }DocuSign
DocuSign manages the formal approval envelope sent to the CFO or designated approver. The envelope contains the comparison summary document and the broker quote. On completion, DocuSign fires a Connect webhook to the orchestration layer, which triggers the broker acceptance email and the Xero bill creation step.
Auth method
OAuth 2.0 JWT Grant (server-to-server, no user interaction required at send time). RSA private key stored as DOCUSIGN_RSA_PRIVATE_KEY in credential store. Integration Key stored as DOCUSIGN_INTEGRATION_KEY. Impersonated user GUID (the sending account) stored as DOCUSIGN_USER_GUID. Account base URI stored as DOCUSIGN_ACCOUNT_BASE_URI.
Required scopes
signature (create and send envelopes). impersonation (required for JWT Grant to act on behalf of a user). extended (access to Connect webhook configuration if managed via API).
Webhook/trigger setup
DocuSign Connect must be configured to POST a completion event to the orchestration layer's inbound webhook endpoint when envelope status transitions to 'completed'. Payload format: JSON. Events subscribed: envelope-completed, envelope-declined, envelope-voided. The inbound endpoint must validate the HMAC signature on each Connect event using the Connect HMAC key stored as DOCUSIGN_CONNECT_HMAC_KEY. Reject any event where the computed HMAC does not match the X-DocuSign-Signature-1 header.
Required configuration
Envelope template created in DocuSign for the approval request containing: Tab - Approve/Decline, Tab - Signature, Tab - Date Signed. Template ID stored as DOCUSIGN_TEMPLATE_ID. Approver email and name passed dynamically from the Notion policy record (CFO field). Comparison summary document attached as a supplemental document on the envelope. Envelope subject line: 'Insurance Renewal Approval: {policy_name} - Expiry {expiry_date}'.
Rate limits
DocuSign Standard plan: 100 envelopes/month included, additional at $0.10 each. At 6 to 20 renewals/year, usage is well under the monthly limit. API call rate: 1,000 calls/hour per account. No throttling required at current volume.
Constraints
JWT Grant requires prior consent from the impersonated user via a one-time browser-based grant flow before the first automated send. RSA key pair must be 2048-bit minimum. DocuSign Connect webhooks are not available on the free Developer sandbox plan; use the DocuSign demo environment (demo.docusign.net) for sandbox testing. Production and demo base URIs differ and must each be stored separately.
// Create and send envelope
POST {DOCUSIGN_ACCOUNT_BASE_URI}/v2.1/accounts/{account_id}/envelopes
Body: { templateId: '{DOCUSIGN_TEMPLATE_ID}', templateRoles: [ { email: '{approver_email}', name: '{approver_name}', roleName: 'Approver' } ], status: 'sent', documents: [ { documentBase64: '<base64_summary>', name: 'Comparison Summary - {policy_name}.pdf', documentId: '1' } ] }
// Inbound Connect event validation
POST /webhooks/docusign-connect
Validate: HMAC-SHA256( DOCUSIGN_CONNECT_HMAC_KEY, raw_body ) == X-DocuSign-Signature-1
On match and status == 'completed': extract envelopeId, trigger downstream stepsXero
Xero receives a draft bill creation request once DocuSign sign-off is confirmed. The bill is assigned to the correct supplier (insurer) and account code. No payment is initiated by the automation; the draft bill enters the standard Xero accounts payable review queue for the Accounts Manager.
Auth method
OAuth 2.0 Authorization Code Grant with offline access (refresh token). Access token expires after 30 minutes; refresh token lasts 60 days. Refresh token must be re-obtained before expiry or a re-authorisation flow must be triggered. Credentials: XERO_CLIENT_ID, XERO_CLIENT_SECRET, XERO_REFRESH_TOKEN, XERO_TENANT_ID all stored in the credential store.
Required scopes
accounting.transactions (create and read invoices and bills). accounting.contacts.read (look up supplier/contact ID by name). accounting.settings.read (read account codes).
Webhook/trigger setup
No inbound Xero webhook is required. The orchestration layer writes to Xero via API only, triggered by the DocuSign completion event. Xero does support outbound event subscriptions (for future use) but these are not needed in this build.
Required configuration
Each insurer must exist as a Supplier Contact in Xero before launch. Contact IDs for all insurers must be stored as key-value pairs in the credential store: XERO_CONTACT_IDS (JSON object keyed by insurer name). The correct account code for insurance premium bills must be confirmed and stored as XERO_INSURANCE_ACCOUNT_CODE. Xero Tenant ID (organisation GUID) stored as XERO_TENANT_ID. Bill due date calculated as invoice date plus the payment terms stored on the supplier Contact in Xero.
Rate limits
Xero API: 60 calls/minute per connection, 5,000 calls/day. At 6 to 20 bills/year, this is negligible. Concurrency limit: 1 connection per tenant. Implement standard retry with backoff on 429 and 503 responses. The refresh token rotation must be handled atomically: on refresh, immediately write the new token back to the credential store before any other operation.
Constraints
Xero OAuth refresh tokens do not rotate on every use by default; however, if the same token is used concurrently from multiple automation nodes, token invalidation can occur. Ensure the orchestration layer serialises Xero token refresh calls. Draft bills created with status DRAFT will not trigger payment runs until manually approved in Xero by the Accounts Manager.
// Create draft bill
POST https://api.xero.com/api.xro/2.0/Invoices
Headers: Authorization: Bearer {access_token}, Xero-tenant-id: {XERO_TENANT_ID}
Body: { Type: 'ACCPAY', Contact: { ContactID: '{XERO_CONTACT_IDS[insurer_name]}' }, Date: '<invoice_date>', DueDate: '<due_date>', Status: 'DRAFT',
LineItems: [ { Description: 'Insurance premium: {policy_name} ({coverage_type})', Quantity: 1, UnitAmount: {premium_amount}, AccountCode: '{XERO_INSURANCE_ACCOUNT_CODE}' } ] }Integration and API SpecPage 2 of 4
FS-DOC-05Technical
03Field mappings between tools
The following tables define the exact field mappings at each agent handoff point. All field names are shown as they appear in each tool's API or data model. These mappings must be implemented precisely in the orchestration layer; any deviation will cause data loss or mis-routing.
Handoff 1: Renewal Tracker Agent — Notion to Gmail (broker outreach)
Source tool
Source field
Destination tool
Destination field
Notion
`properties.Policy Name.title[0].text.content`
Gmail
`subject` (interpolated: 'Renewal Request: {value}')
Notion
`properties.Expiry Date.date.start`
Gmail
`subject` and `body` (interpolated: 'Expiry: {value}')
Notion
`properties.Broker Email.email`
Gmail
`to`
Notion
`properties.Broker Name.rich_text[0].text.content`
Gmail
`body` salutation and `GDRIVE_OUTREACH_TEMPLATE_ID` placeholder `{{broker_name}}`
Notion
`properties.Policy Number.rich_text[0].text.content`
Gmail
`body` placeholder `{{policy_number}}`
Notion
`properties.Coverage Type.select.name`
Gmail
`body` placeholder `{{coverage_type}}`
Notion
`id` (page ID)
HubSpot
`properties.renewal_chase_status` context reference stored in engagement note
Handoff 2: Renewal Tracker Agent — Notion and Gmail to HubSpot (outreach logging)
Source tool
Source field
Destination tool
Destination field
Notion
`properties.Broker Email.email`
HubSpot
`contact.email` (used to look up Contact ID via GET /crm/v3/objects/contacts?email={value})
Notion
`properties.Policy Name.title[0].text.content`
HubSpot
`engagement.metadata.subject` (email log subject)
Gmail (sent)
`message.internalDate`
HubSpot
`properties.last_renewal_outreach_date`
Orchestration layer
`current_chase_sequence_number`
HubSpot
`properties.renewal_chase_status` (mapped: 1=Outreach Sent, 2=Chase 1 Sent, 3=Chase 2 Sent)
Handoff 3: Quote Comparison Agent — Gmail intake and Notion to Slack (comparison summary)
Source tool
Source field
Destination tool
Destination field
Gmail (intake)
`message.payload.parts[].filename`
Google Drive
`file.name` (stored in `GDRIVE_QUOTE_FOLDER_ID`)
Google Drive
`file.id`
Notion
`properties.Quote Document URL.url` (constructed as Drive file link)
AI extraction output
`extracted.new_premium`
Slack
Block Kit text: 'New premium: ${value}'
AI extraction output
`extracted.prior_premium`
Slack
Block Kit text: 'Prior premium: ${value}'
AI extraction output
`extracted.premium_delta_pct`
Slack
Block Kit text: 'Premium change: {value}%'
AI extraction output
`extracted.coverage_flags[]`
Slack
Block Kit section list: each flag as a bullet
AI extraction output
`extracted.recommendation`
Slack
Block Kit text: plain-English recommendation string
Notion
`properties.Policy Name.title[0].text.content`
Slack
Block Kit header: 'Renewal Summary: {value}'
Handoff 4: Approval and Completion Agent — Slack approval confirmation to DocuSign (envelope creation)
Source tool
Source field
Destination tool
Destination field
Notion
`properties.Policy Name.title[0].text.content`
DocuSign
`emailSubject` (interpolated: 'Insurance Renewal Approval: {value}')
Notion
`properties.Expiry Date.date.start`
DocuSign
`emailSubject` suffix and supplemental document metadata
Notion
`properties.CFO Email.email`
DocuSign
`templateRoles[0].email`
Notion
`properties.CFO Name.rich_text[0].text.content`
DocuSign
`templateRoles[0].name`
Google Drive
`file.id` (comparison summary PDF)
DocuSign
`documents[0].documentBase64` (base64-encoded file content)
Handoff 5: Approval and Completion Agent — DocuSign completion to Gmail, Xero, and Notion (close loop)
Source tool
Source field
Destination tool
Destination field
DocuSign Connect event
`envelopeId`
Notion
`properties.DocuSign Envelope ID.rich_text[0].text.content`
DocuSign Connect event
`completedDateTime`
Notion
`properties.Renewal Status.select.name` set to 'Renewed'
Notion
`properties.Broker Email.email`
Gmail
`to` (acceptance email)
Notion
`properties.Policy Name.title[0].text.content`
Gmail
`subject` placeholder `{{policy_name}}`
AI extraction output
`extracted.new_premium`
Xero
`LineItems[0].UnitAmount`
Notion
`properties.Coverage Type.select.name`
Xero
`LineItems[0].Description` (suffix)
Credential store
`XERO_CONTACT_IDS[insurer_name]`
Xero
`Contact.ContactID`
Credential store
`XERO_INSURANCE_ACCOUNT_CODE`
Xero
`LineItems[0].AccountCode`
Xero response
`Invoices[0].InvoiceID`
Notion
`properties.Xero Bill ID.rich_text[0].text.content`
AI extraction output
`extracted.next_renewal_date`
Notion
`properties.Next Renewal Date.date.start`
Integration and API SpecPage 3 of 4
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Three discrete workflows, one per agent (Renewal Tracker, Quote Comparison, Approval and Completion). Each workflow is independently deployable and maintainable. Shared credential store accessed by all three workflows via environment variable injection at runtime.
Agent 1 trigger mechanism
Scheduled poll (cron: daily at 06:00 UTC). The orchestration layer calls the Notion database query endpoint with a date-range filter. No webhook registration required. If the poll discovers a policy in the expiry window that has not yet had outreach sent (renewal_chase_status == 'Not Started'), the outreach branch is executed. If a chase timer has elapsed (last_renewal_outreach_date older than 7 days and status != 'Quote Received'), the chase branch fires.
Agent 2 trigger mechanism
Gmail push notification via Google Cloud Pub/Sub (preferred) or scheduled 15-minute poll of the intake inbox. On receipt of a new message with a PDF or DOCX attachment in the intake mailbox, the workflow extracts the attachment, stores it in Google Drive, runs AI extraction, and posts the comparison summary to Slack. HMAC or Pub/Sub message ID validation must be applied to prevent duplicate processing.
Agent 3 trigger mechanism
Inbound webhook from DocuSign Connect. The orchestration layer exposes a secure HTTPS endpoint. On receipt of a Connect event with envelope status 'completed', HMAC-SHA256 signature validation is performed using DOCUSIGN_CONNECT_HMAC_KEY. Only after successful validation does the workflow proceed to send the broker acceptance email, create the Xero bill, and update Notion.
Credential store structure
All secrets stored as encrypted environment variables in the orchestration platform's native credential store. No secrets hardcoded in workflow definitions or version-controlled configuration files. Separate credential sets maintained for sandbox and production environments, clearly namespaced (e.g. PROD_ prefix for production values).
Inter-workflow communication
Agent 2 is triggered by Gmail intake, not by a direct signal from Agent 1. Agent 3 is triggered by the DocuSign Connect webhook, not by a direct signal from Agent 2. Workflows are decoupled; state is passed via Notion page properties and the credential store, not via in-memory calls between workflows.
Credential store: all 28 keys must be populated in the sandbox environment before integration testing begins, then replicated to production with live values at go-live.
// Credential store: all keys required before go-live
// Notion
NOTION_API_TOKEN=<internal_integration_token>
NOTION_POLICY_DB_ID=<database_guid>
// Google Workspace
GMAIL_SERVICE_ACCOUNT_KEY_JSON=<base64_encoded_service_account_json>
GMAIL_DELEGATE_ADDRESS=renewals@[YourCompany.com]
GMAIL_INTAKE_ADDRESS=renewals@[YourCompany.com]
GDRIVE_QUOTE_FOLDER_ID=<drive_folder_id>
GDRIVE_OUTREACH_TEMPLATE_ID=<google_doc_id>
GDRIVE_ACCEPTANCE_TEMPLATE_ID=<google_doc_id>
GCLOUD_PUBSUB_TOPIC=<projects/{project_id}/topics/{topic_name}>
// HubSpot
HUBSPOT_PRIVATE_APP_TOKEN=<hubspot_pat>
HUBSPOT_PORTAL_ID=<portal_id>
HUBSPOT_CHASE_STATUS_PROP=renewal_chase_status
HUBSPOT_OUTREACH_DATE_PROP=last_renewal_outreach_date
// Slack
SLACK_BOT_TOKEN=xoxb-<token>
SLACK_COMPLIANCE_CHANNEL_ID=C<channel_id>
SLACK_WORKSPACE_ID=T<workspace_id>
// DocuSign
DOCUSIGN_INTEGRATION_KEY=<client_id_guid>
DOCUSIGN_RSA_PRIVATE_KEY=<base64_pem_private_key>
DOCUSIGN_USER_GUID=<impersonated_user_guid>
DOCUSIGN_ACCOUNT_BASE_URI=https://na4.docusign.net // or regional URI from /oauth/userinfo
DOCUSIGN_TEMPLATE_ID=<envelope_template_guid>
DOCUSIGN_CONNECT_HMAC_KEY=<connect_hmac_secret>
// Xero
XERO_CLIENT_ID=<oauth_client_id>
XERO_CLIENT_SECRET=<oauth_client_secret>
XERO_REFRESH_TOKEN=<refresh_token> // must be updated on each refresh
XERO_TENANT_ID=<organisation_guid>
XERO_INSURANCE_ACCOUNT_CODE=<e.g. 625>
XERO_CONTACT_IDS={"Insurer A": "<xero_contact_guid>", "Insurer B": "<xero_contact_guid>"}
// Inbound webhook endpoint (hosted by orchestration layer)
DOCUSIGN_CONNECT_INBOUND_URL=https://<orchestration_host>/webhooks/docusign-connect
GMAIL_PUBSUB_PUSH_ENDPOINT=https://<orchestration_host>/webhooks/gmail-intake05Error handling and retry logic
Unhandled exceptions must never fail silently. Every integration point must be covered by the logic below. If an error cannot be resolved by the defined retry or fallback behaviour, the orchestration layer must post an alert to SLACK_COMPLIANCE_CHANNEL_ID and log a structured error entry (timestamp, workflow name, step name, error code, payload reference) to the centralised error log.
Integration
Scenario
Required behaviour
Notion (poll)
API returns 429 (rate limit)
Exponential backoff: wait 2s, 4s, 8s for up to 3 retries. If all retries fail, skip the current poll cycle and alert Slack. Do not discard the policy record; it will be picked up in the next scheduled poll.
Notion (poll)
Database query returns zero results unexpectedly
Log a warning to the error log and alert Slack if zero results persist for 3 consecutive daily polls. This may indicate the database ID has changed or the integration token has lost access.
Gmail (send outreach)
Send fails with 4xx (invalid recipient address)
Do not retry. Post an alert to Slack with the policy name and the invalid email address. Set HubSpot renewal_chase_status to 'Not Started' to prevent false chase timers. Require manual correction of the broker email in Notion before the next poll.
Gmail (send outreach)
Send fails with 5xx (Gmail service error)
Retry up to 3 times with 30-second intervals. If all retries fail, post Slack alert and add the outreach task to a dead-letter queue for manual send review. Log the full error response.
Gmail (intake poll / Pub/Sub)
Duplicate message delivery detected (same message ID processed twice)
Maintain a deduplication cache of processed Gmail message IDs for a rolling 48-hour window. On receipt of a known message ID, discard silently and log at DEBUG level. Do not trigger Quote Comparison Agent twice for the same attachment.
HubSpot (engagement log)
Contact not found for broker email
Attempt to create a new Contact record with the broker email and name from Notion. If creation fails (e.g. duplicate detection), log the error and post a Slack alert asking the team to manually link the broker in HubSpot. Do not block the outreach email from sending.
Slack (post message)
Bot not in channel or invalid channel ID
Catch the channel_not_found or not_in_channel error. Post a fallback direct message to the workspace admin user ID (stored as SLACK_ADMIN_USER_ID). Log the error. Do not silently drop the comparison summary.
DocuSign (envelope send)
JWT token consent not granted or token expired
Catch the consent_required or invalid_grant error. Post a Slack alert with instructions to re-run the one-time consent URL. Block the approval flow until the token is re-authorised. Do not send a partial or unsigned envelope.
DocuSign Connect (inbound webhook)
HMAC signature validation fails
Return HTTP 400 to DocuSign. Log the raw payload hash and the received signature. Do not process the event. Alert Slack with the envelope ID from the payload (if parseable). Investigate potential HMAC key mismatch or replay attack before re-enabling.
DocuSign Connect (inbound webhook)
Envelope status is 'declined' or 'voided'
Do not trigger broker acceptance or Xero bill creation. Post a Slack alert: 'Approval declined or voided for {policy_name}. Manual intervention required.' Update Notion Renewal Status to 'In Progress' and await human instruction.
Xero (bill creation)
Refresh token expired or invalid
Catch the invalid_grant or token_expired error. Post a Slack alert and halt the Xero step. The broker acceptance email has already been sent at this point; log the bill details to the error log so accounts can create the bill manually. Trigger re-authorisation flow at next opportunity.
Xero (bill creation)
ContactID not found for insurer name
Do not create the bill with a blank or incorrect contact. Post a Slack alert with the insurer name that failed the lookup. Log the premium amount and policy name so accounts can create the bill manually. Update XERO_CONTACT_IDS in the credential store before the next renewal cycle.
Notion (final record update)
PATCH request fails after DocuSign and Xero steps complete
Retry up to 3 times with 10-second intervals. If all retries fail, log the intended field values (Renewal Status, Xero Bill ID, DocuSign Envelope ID, Next Renewal Date) to the error log and post a Slack alert. The downstream steps are already complete; this is a data consistency risk only and requires a manual Notion update to resolve.
For support during build or post-launch, contact the FullSpec team at support@gofullspec.com. Include the workflow name, error log reference, and the timestamp of the failing event in your message.
Integration and API SpecPage 4 of 4