FS-DOC-05Technical
Integration and API Spec
Client / Customer Onboarding Automation
[YourCompany.com] · Operations Department · Prepared by FullSpec · [Today's Date]
This document is the definitive integration reference for the Client Onboarding automation. It covers every tool connected to the orchestration layer, the exact authentication method and OAuth scopes required for each, field-level mappings between tools, credential store structure, and the full error-handling and retry policy. The FullSpec team uses this specification to configure, test, and maintain all connections. No credentials or IDs should be hardcoded in workflow logic; all values reference the shared credential store defined in Section 04.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
Orchestration layer
Hosts all workflow logic, credential store, and inter-agent routing
Internal service account / env vars
N/A (build platform)
All
HubSpot
CRM trigger source; contact and deal record read/write; deal stage updates
OAuth 2.0 (private app token)
Starter CRM (API access required)
Agent 1, Agent 2
Gmail
Welcome email and reminder email delivery
OAuth 2.0 (Google Workspace)
Google Workspace Business Starter
Agent 1
Typeform
Intake form dispatch and response capture via webhook
OAuth 2.0 / Personal Access Token
Basic (webhooks included)
Agent 1
Xero
Invoice creation and dispatch to billing contact
OAuth 2.0 (Xero app)
Starter or above
Agent 1
Slack
Internal new-client notification to onboarding channel
OAuth 2.0 (Slack app bot token)
Free tier sufficient; Pro for guaranteed delivery
Agent 2
DocuSign
Contract signed event source (optional supplementary trigger)
OAuth 2.0 (JWT grant)
Standard or above
Agent 1 (trigger enrichment only)
Before you connect anything: sandbox-test every integration against a non-production environment before any production credentials are entered. For HubSpot, use a developer sandbox account. For Xero, use the Xero demo company. For Typeform, use a duplicate test form. For Gmail, use a dedicated onboarding-test@[YourCompany.com] mailbox. For Slack, post to a private #onboarding-test channel. Only after all sandbox tests pass should production credentials be entered into the credential store.
02Per-tool integration detail
HubSpot
Primary trigger source and CRM record store. The orchestration layer polls HubSpot for deal stage changes to Closed Won and writes intake data back to contact and company records. Deal stage is also updated to Onboarded at the close of the sequence.
Auth method
OAuth 2.0 via HubSpot Private App token. Token generated inside HubSpot Settings > Integrations > Private Apps. Store token as HUBSPOT_PRIVATE_APP_TOKEN in credential store. Token does not expire but must be rotated if scopes change.
Required scopes
crm.objects.deals.read, crm.objects.deals.write, crm.objects.contacts.read, crm.objects.contacts.write, crm.objects.companies.read, crm.objects.companies.write, crm.schemas.deals.read, crm.schemas.contacts.read, timeline.write
Webhook / trigger setup
Configure a HubSpot Workflow inside HubSpot to fire an outbound webhook POST to the orchestration layer inbound URL when dealstage = closedwon. Alternatively, the orchestration layer polls GET /crm/v3/objects/deals with a filter on hs_deal_stage_probability = 1 every 5 minutes. Webhook is preferred for latency. Validate the X-HubSpot-Signature-v3 header using HMAC-SHA256 against HUBSPOT_WEBHOOK_SECRET.
Required configuration
Pipeline ID for the active sales pipeline stored as HUBSPOT_PIPELINE_ID. Closed Won stage ID stored as HUBSPOT_CLOSEDWON_STAGE_ID. Onboarded stage ID stored as HUBSPOT_ONBOARDED_STAGE_ID. Custom contact properties for intake fields must be pre-created in HubSpot before go-live (see Section 03 for full property list). No IDs hardcoded in workflow logic.
Rate limits
HubSpot enforces 100 requests/10 seconds and 40,000 requests/day on Starter. At 20 clients/month (~220 automation runs/month), peak concurrent requests remain well under limit. Throttling not required at current volume, but implement a 200 ms delay between sequential API calls within a single run to avoid burst violations.
Constraints
Private App tokens are workspace-scoped; a separate token is required for each HubSpot portal. Custom properties must exist before the workflow attempts to write to them or the write will silently fail. Deal association to contact and company must be present for the trigger payload to carry the required IDs.
// Trigger payload (inbound from HubSpot webhook)
{
"objectId": "<deal_id>",
"propertyName": "dealstage",
"propertyValue": "closedwon",
"portalId": "<HUBSPOT_PORTAL_ID>",
"associatedObjectId": "<contact_id>"
}
// Write back (PATCH /crm/v3/objects/contacts/{contact_id})
{
"properties": {
"intake_business_name": "<value>",
"intake_billing_email": "<value>",
"intake_service_tier": "<value>",
"intake_payment_terms": "<value>",
"onboarding_status": "onboarded"
}
}Gmail
Sends the personalised welcome email to the new client and the automated 48-hour reminder if the intake form is not submitted. Both emails are sent from the assigned account manager's address using Gmail send-as delegation.
Auth method
OAuth 2.0 via Google Workspace. Authorise the orchestration layer service account with delegated access to the sending mailbox. Store refresh token as GMAIL_OAUTH_REFRESH_TOKEN and client credentials as GMAIL_CLIENT_ID and GMAIL_CLIENT_SECRET.
Required scopes
https://www.googleapis.com/auth/gmail.send, https://www.googleapis.com/auth/gmail.compose
Webhook / trigger setup
No inbound webhook required. Gmail is an outbound-only integration in this flow. The orchestration layer calls POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send with a base64url-encoded RFC 2822 message body.
Required configuration
Sender email address stored as GMAIL_SENDER_ADDRESS. Welcome email template stored as GMAIL_WELCOME_TEMPLATE_ID (template body stored in credential store or CMS, not hardcoded). Reminder email template stored as GMAIL_REMINDER_TEMPLATE_ID. Template placeholders: {{client_first_name}}, {{service_tier}}, {{account_manager_name}}, {{intake_form_url}}, {{account_manager_email}}.
Rate limits
Gmail API allows 250 quota units per user per second; a send costs 100 units. At 20 clients/month and a maximum of 2 emails per client (welcome plus one reminder), peak is 40 sends/month, far below any limit. Throttling not required.
Constraints
Send-as delegation must be granted by a Google Workspace admin before go-live. If the account manager changes, GMAIL_SENDER_ADDRESS must be updated. Attachments are not included in the welcome email by default; if a PDF welcome pack is required, it must be base64-encoded and appended to the MIME payload.
// Outbound welcome email payload
{
"to": "<client_primary_email>",
"from": "<GMAIL_SENDER_ADDRESS>",
"subject": "Welcome to [YourCompany.com] - here is what happens next",
"body_html": "<rendered from GMAIL_WELCOME_TEMPLATE_ID>",
"reply_to": "<account_manager_email>"
}
// Reminder email triggered at T+48h if Typeform not submitted
{
"to": "<client_primary_email>",
"subject": "Quick reminder: we still need your details",
"body_html": "<rendered from GMAIL_REMINDER_TEMPLATE_ID>"
}Typeform
Hosts the client intake form. The orchestration layer sends the unique form URL to the client via Gmail, then listens for a Typeform webhook POST when the form is submitted. Response data is extracted and passed to the HubSpot and Xero steps.
Auth method
Personal Access Token for API calls (form URL retrieval, response lookup). Store as TYPEFORM_API_TOKEN. Webhook signature validation uses TYPEFORM_WEBHOOK_SECRET (set when registering the webhook endpoint in Typeform).
Required scopes
forms:read, responses:read, webhooks:write, webhooks:read (Personal Access Token scope selection in Typeform account settings)
Webhook / trigger setup
Register a webhook on the intake form via POST https://api.typeform.com/forms/{TYPEFORM_FORM_ID}/webhooks with the orchestration layer inbound URL as the destination. Set enabled: true and secret to TYPEFORM_WEBHOOK_SECRET. Validate incoming payloads by checking the Typeform-Signature header (SHA256 HMAC). The webhook fires on every form submission.
Required configuration
Form ID stored as TYPEFORM_FORM_ID. Field reference IDs for each intake question stored in the credential store (see Section 03 for full mapping). The intake form must contain fields for: business name, billing contact name, billing email, service tier, payment terms, primary contact phone, and timezone. TYPEFORM_FORM_URL constructed as https://[YourCompany.typeform.com]/to/{TYPEFORM_FORM_ID}.
Rate limits
Typeform API is rate-limited to 2 requests/second and 60,000 requests/day on paid plans. Webhook delivery is not rate-limited. At 20 submissions/month, API usage is negligible. Throttling not required.
Constraints
If the intake form is modified after go-live, field reference IDs may change. Any form edit must be reviewed against the field mapping table in Section 03 before deployment. Typeform does not guarantee webhook delivery order; the orchestration layer must handle duplicate submission events idempotently using the response_id as a deduplication key.
// Inbound webhook payload (on form submission)
{
"event_id": "<uuid>",
"form_response": {
"form_id": "<TYPEFORM_FORM_ID>",
"token": "<response_id>",
"submitted_at": "<ISO8601>",
"answers": [
{"field": {"ref": "business_name"}, "text": "<value>"},
{"field": {"ref": "billing_email"}, "email": "<value>"},
{"field": {"ref": "service_tier"}, "choice": {"label": "<value>"}},
{"field": {"ref": "payment_terms"}, "choice": {"label": "<value>"}},
{"field": {"ref": "billing_contact_name"}, "text": "<value>"},
{"field": {"ref": "primary_phone"}, "phone_number": "<value>"},
{"field": {"ref": "timezone"}, "text": "<value>"}
]
}
}Integration and API SpecPage 1 of 3
FS-DOC-05Technical
Xero
Creates and dispatches the first invoice to the client's billing contact. Invoice data is assembled from the HubSpot deal record (deal value, payment terms) and the Typeform intake response (billing contact name and email). The invoice is sent automatically once created unless draft-only mode is enabled.
Auth method
OAuth 2.0 via a Xero connected app. Authorise once using the authorization code flow; store refresh token as XERO_OAUTH_REFRESH_TOKEN, client ID as XERO_CLIENT_ID, and client secret as XERO_CLIENT_SECRET. Access tokens expire after 30 minutes; the orchestration layer must refresh automatically before each Xero API call. Tenant ID stored as XERO_TENANT_ID.
Required scopes
accounting.transactions, accounting.contacts, accounting.contacts.read, accounting.settings.read, offline_access
Webhook / trigger setup
No inbound webhook. Xero is a write-only integration in this flow. The orchestration layer calls POST https://api.xero.com/api.xro/2.0/Invoices to create the invoice. If a matching Xero contact does not exist, the orchestration layer first calls POST https://api.xero.com/api.xro/2.0/Contacts to create the contact.
Required configuration
Xero account code for onboarding services stored as XERO_REVENUE_ACCOUNT_CODE (e.g. 200). Default tax type stored as XERO_TAX_TYPE (e.g. OUTPUT2 for standard-rated). Invoice due-date offset in days stored as XERO_PAYMENT_TERMS_DAYS. Draft-only mode flag stored as XERO_DRAFT_MODE (true/false). No account codes or tax types hardcoded in workflow logic.
Rate limits
Xero enforces 60 API calls/minute and 5,000 calls/day. At 20 invoices/month, peak usage is negligible. Throttling not required, but implement exponential backoff on 429 responses.
Constraints
A Xero contact must exist or be created before an invoice can be raised; mismatched billing emails cause duplicate contact creation. The automation checks for an existing contact by email before creating a new one. If XERO_DRAFT_MODE is true, the invoice is created as DRAFT and must be approved manually before Xero sends it. This is the recommended default for the first month of live operation.
// POST /api.xro/2.0/Invoices
{
"Type": "ACCREC",
"Contact": {"EmailAddress": "<billing_email>"},
"DueDate": "<today + XERO_PAYMENT_TERMS_DAYS>",
"Status": "<DRAFT if XERO_DRAFT_MODE else AUTHORISED>",
"LineItems": [
{
"Description": "Onboarding - <service_tier>",
"Quantity": 1,
"UnitAmount": <hs_deal_amount>,
"AccountCode": "<XERO_REVENUE_ACCOUNT_CODE>",
"TaxType": "<XERO_TAX_TYPE>"
}
]
}Slack
Posts a structured new-client notification to the designated onboarding Slack channel when the intake form submission is confirmed and the HubSpot record is updated. Used by the Internal Handoff Agent (Agent 2).
Auth method
OAuth 2.0 Slack App with bot token scope. Install the Slack app to the workspace and store the bot token as SLACK_BOT_TOKEN. Do not use legacy incoming webhook URLs for new builds.
Required scopes
chat:write, chat:write.public, channels:read (bot token scopes)
Webhook / trigger setup
No inbound webhook. Slack is outbound-only in this flow. The orchestration layer calls POST https://slack.com/api/chat.postMessage with the bot token in the Authorization header. Channel ID stored as SLACK_ONBOARDING_CHANNEL_ID (not channel name, which can change).
Required configuration
Channel ID stored as SLACK_ONBOARDING_CHANNEL_ID. Message template uses Slack Block Kit JSON for structured layout. Kickoff scheduling link stored as SLACK_KICKOFF_URL_TEMPLATE (parameterised by account manager). Bot display name and icon configured in Slack app settings, not in the API payload.
Rate limits
Slack enforces 1 message/second per channel via the Web API (Tier 3 method). At 20 notifications/month, rate limit is not a concern. Throttling not required.
Constraints
The bot must be invited to SLACK_ONBOARDING_CHANNEL_ID before go-live or chat.postMessage will return channel_not_found. If the channel is renamed, the stored channel ID remains valid; update SLACK_ONBOARDING_CHANNEL_ID only if the channel is deleted and recreated.
// POST https://slack.com/api/chat.postMessage
{
"channel": "<SLACK_ONBOARDING_CHANNEL_ID>",
"blocks": [
{"type": "header", "text": {"type": "plain_text", "text": "New client onboarded"}},
{"type": "section", "fields": [
{"type": "mrkdwn", "text": "*Client:*\n<intake_business_name>"},
{"type": "mrkdwn", "text": "*Tier:*\n<intake_service_tier>"},
{"type": "mrkdwn", "text": "*Account Manager:*\n<account_manager_name>"},
{"type": "mrkdwn", "text": "*Kickoff link:*\n<SLACK_KICKOFF_URL_TEMPLATE>"}
]}
]
}DocuSign
Optional supplementary trigger source. A completed envelope event from DocuSign can serve as an alternative or parallel trigger alongside the HubSpot Closed Won stage change. Used for enrichment only in this build; the HubSpot trigger is the primary source.
Auth method
OAuth 2.0 JWT Grant. Generate an RSA key pair in the DocuSign developer portal; store private key as DOCUSIGN_RSA_PRIVATE_KEY, integration key as DOCUSIGN_INTEGRATION_KEY, and impersonated user ID as DOCUSIGN_USER_ID. Store base URL as DOCUSIGN_BASE_URL (production: https://na4.docusign.net).
Required scopes
signature, impersonation
Webhook / trigger setup
Configure a DocuSign Connect webhook (Envelope Completed event) in the DocuSign admin panel to POST to the orchestration layer inbound URL. Validate the X-DocuSign-Signature-1 header using HMAC-SHA256 against DOCUSIGN_CONNECT_SECRET. Use this event to confirm contract completion if HubSpot deal stage updates are delayed.
Required configuration
Account ID stored as DOCUSIGN_ACCOUNT_ID. Template ID for the onboarding contract stored as DOCUSIGN_TEMPLATE_ID. Connect webhook listener URL registered in DocuSign admin and stored as DOCUSIGN_CONNECT_LISTENER_URL for reference.
Rate limits
DocuSign production accounts allow 1,000 API calls/hour. At 20 contracts/month, usage is negligible. Throttling not required.
Constraints
JWT consent must be granted by the impersonated user before the first API call. DocuSign Connect webhooks are not guaranteed to be in order; use envelope status as a filter and treat duplicate events idempotently using the envelopeId.
// Inbound DocuSign Connect event (envelope completed)
{
"envelopeId": "<uuid>",
"status": "completed",
"emailSubject": "<contract subject>",
"recipients": {
"signers": [{
"email": "<client_primary_email>",
"name": "<client_name>",
"status": "completed"
}]
}
}03Field mappings between tools
The tables below define the exact field mappings for each agent handoff. All field names are shown in monospace as they appear in the respective tool API payloads. These mappings must be configured in the orchestration layer before go-live and verified during sandbox testing.
Handoff 1: HubSpot deal trigger to Onboarding Coordinator Agent (Agent 1) context object
Source tool
Source field
Destination tool
Destination field
HubSpot
deal.id
Orchestration context
ctx.deal_id
HubSpot
deal.dealname
Orchestration context
ctx.deal_name
HubSpot
deal.amount
Orchestration context
ctx.deal_amount
HubSpot
deal.hubspot_owner_id
Orchestration context
ctx.account_manager_id
HubSpot
contact.email
Orchestration context
ctx.client_email
HubSpot
contact.firstname
Orchestration context
ctx.client_first_name
HubSpot
contact.lastname
Orchestration context
ctx.client_last_name
HubSpot
deal.hs_deal_stage_probability
Orchestration context
ctx.deal_stage_confirmed
Handoff 2: Orchestration context to Gmail (welcome email template merge)
Source tool
Source field
Destination tool
Destination field
Orchestration context
ctx.client_email
Gmail
to
Orchestration context
ctx.client_first_name
Gmail template
{{client_first_name}}
Orchestration context
ctx.account_manager_name
Gmail template
{{account_manager_name}}
Orchestration context
ctx.service_tier
Gmail template
{{service_tier}}
Orchestration context
ctx.intake_form_url
Gmail template
{{intake_form_url}}
Orchestration context
ctx.account_manager_email
Gmail
reply_to
Handoff 3: Typeform submission webhook to HubSpot contact record update
Source tool
Source field
Destination tool
Destination field
Typeform
answers[ref=business_name].text
HubSpot
contact.intake_business_name
Typeform
answers[ref=billing_email].email
HubSpot
contact.intake_billing_email
Typeform
answers[ref=billing_contact_name].text
HubSpot
contact.intake_billing_contact_name
Typeform
answers[ref=service_tier].choice.label
HubSpot
contact.intake_service_tier
Typeform
answers[ref=payment_terms].choice.label
HubSpot
contact.intake_payment_terms
Typeform
answers[ref=primary_phone].phone_number
HubSpot
contact.phone
Typeform
answers[ref=timezone].text
HubSpot
contact.hs_timezone
Typeform
form_response.token
HubSpot
contact.typeform_response_id
Handoff 4: HubSpot and Typeform data to Xero invoice creation
Source tool
Source field
Destination tool
Destination field
Typeform
answers[ref=billing_email].email
Xero
Invoices[0].Contact.EmailAddress
Typeform
answers[ref=billing_contact_name].text
Xero
Invoices[0].Contact.Name
HubSpot
deal.amount
Xero
Invoices[0].LineItems[0].UnitAmount
Typeform
answers[ref=payment_terms].choice.label
Xero
Invoices[0].DueDateOffset (mapped via XERO_PAYMENT_TERMS_DAYS)
HubSpot
contact.intake_service_tier
Xero
Invoices[0].LineItems[0].Description
Handoff 5: HubSpot and Typeform data to Slack new-client notification (Internal Handoff Agent)
Source tool
Source field
Destination tool
Destination field
Typeform
answers[ref=business_name].text
Slack Block Kit
blocks[1].fields[0].text (Client)
Typeform
answers[ref=service_tier].choice.label
Slack Block Kit
blocks[1].fields[1].text (Tier)
HubSpot
contact.hubspot_owner_name
Slack Block Kit
blocks[1].fields[2].text (Account Manager)
Orchestration context
ctx.kickoff_url
Slack Block Kit
blocks[1].fields[3].text (Kickoff link)
All HubSpot custom contact properties listed in Handoff 3 (intake_business_name, intake_billing_email, intake_billing_contact_name, intake_service_tier, intake_payment_terms, typeform_response_id) must be created as custom properties in HubSpot before the first live run. Property type for all text fields: single-line text. Property type for intake_service_tier and intake_payment_terms: enumeration (matching the Typeform choice labels exactly). Mismatched property types will cause silent write failures.
Integration and API SpecPage 2 of 3
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
One workflow per agent. Agent 1 (Onboarding Coordinator Agent) is a single workflow triggered by the HubSpot Closed Won webhook. Agent 2 (Internal Handoff Agent) is a separate workflow triggered by the Typeform submission webhook. Both workflows share a single credential store; no credentials are duplicated between workflows. Inter-agent communication occurs via shared context variables written to the orchestration platform's internal data store after each agent completes its primary steps.
Agent 1 trigger mechanism
Webhook (preferred): inbound POST from HubSpot Workflow on deal stage = closedwon. Validated via X-HubSpot-Signature-v3 HMAC-SHA256 against HUBSPOT_WEBHOOK_SECRET. Fallback: polling GET /crm/v3/objects/deals every 5 minutes with dealstage filter. Webhook preferred for sub-minute latency. Duplicate trigger prevention: store processed deal_id in orchestration data store and skip re-runs for the same ID within 1 hour.
Agent 2 trigger mechanism
Webhook: inbound POST from Typeform on intake form submission. Validated via Typeform-Signature HMAC-SHA256 against TYPEFORM_WEBHOOK_SECRET. Deduplication: skip if form_response.token already processed (stored in orchestration data store). Agent 2 fires only after Agent 1 has confirmed the matching deal_id is in an active onboarding state; a guard condition checks for ctx.onboarding_active = true before proceeding.
Retry policy
All outbound API calls use exponential backoff: initial delay 2 seconds, multiplier 2, max 3 retries. After 3 failed retries, the step is marked failed and an alert is sent to the operations owner via email and Slack (see Section 05 for per-integration error behaviour).
Logging
Every workflow run logs: run_id, trigger timestamp, deal_id, step name, HTTP status code, and outcome (success/failure). Logs retained for 30 days in the orchestration platform. Failed run payloads retained for manual replay.
Credential store contents (all values stored as encrypted environment variables in the orchestration platform credential store; none hardcoded in workflow logic):
All credential store keys. Populated once during setup; never referenced by literal value in workflow nodes.
# HubSpot
HUBSPOT_PRIVATE_APP_TOKEN=<hubspot_private_app_token>
HUBSPOT_WEBHOOK_SECRET=<hmac_secret_for_signature_validation>
HUBSPOT_PORTAL_ID=<hubspot_portal_id>
HUBSPOT_PIPELINE_ID=<active_sales_pipeline_id>
HUBSPOT_CLOSEDWON_STAGE_ID=<closed_won_stage_id>
HUBSPOT_ONBOARDED_STAGE_ID=<onboarded_stage_id>
# Gmail
GMAIL_CLIENT_ID=<google_oauth_client_id>
GMAIL_CLIENT_SECRET=<google_oauth_client_secret>
GMAIL_OAUTH_REFRESH_TOKEN=<gmail_oauth_refresh_token>
GMAIL_SENDER_ADDRESS=onboarding@[YourCompany.com]
GMAIL_WELCOME_TEMPLATE_ID=<welcome_email_template_id>
GMAIL_REMINDER_TEMPLATE_ID=<reminder_email_template_id>
# Typeform
TYPEFORM_API_TOKEN=<typeform_personal_access_token>
TYPEFORM_FORM_ID=<intake_form_id>
TYPEFORM_WEBHOOK_SECRET=<hmac_secret_for_typeform_signature>
TYPEFORM_FORM_URL=https://[YourCompany].typeform.com/to/<TYPEFORM_FORM_ID>
# Xero
XERO_CLIENT_ID=<xero_app_client_id>
XERO_CLIENT_SECRET=<xero_app_client_secret>
XERO_OAUTH_REFRESH_TOKEN=<xero_oauth_refresh_token>
XERO_TENANT_ID=<xero_organisation_tenant_id>
XERO_REVENUE_ACCOUNT_CODE=200
XERO_TAX_TYPE=OUTPUT2
XERO_PAYMENT_TERMS_DAYS=14
XERO_DRAFT_MODE=true
# Slack
SLACK_BOT_TOKEN=xoxb-<slack_bot_token>
SLACK_ONBOARDING_CHANNEL_ID=<channel_id_not_channel_name>
SLACK_KICKOFF_URL_TEMPLATE=https://calendly.com/[YourCompany]/kickoff
# DocuSign (optional trigger enrichment)
DOCUSIGN_INTEGRATION_KEY=<docusign_integration_key>
DOCUSIGN_USER_ID=<docusign_impersonated_user_id>
DOCUSIGN_RSA_PRIVATE_KEY=<base64_encoded_rsa_private_key>
DOCUSIGN_ACCOUNT_ID=<docusign_account_id>
DOCUSIGN_TEMPLATE_ID=<onboarding_contract_template_id>
DOCUSIGN_CONNECT_SECRET=<hmac_secret_for_connect_validation>
DOCUSIGN_BASE_URL=https://na4.docusign.net
05Error handling and retry logic
Every integration point in the onboarding automation has a defined failure behaviour. Unhandled exceptions must never fail silently. Where retries are exhausted, a human-readable alert is dispatched to the process owner and the failed run is flagged for manual intervention. The table below covers all defined failure scenarios across both agents.
Integration
Scenario
Required behaviour
HubSpot (trigger)
Webhook signature validation fails
Reject payload with HTTP 401. Log invalid request with IP and timestamp. Do not start the workflow. Alert operations owner via email if 3 or more invalid requests occur within 10 minutes (potential replay attack).
HubSpot (trigger)
Trigger fires but deal has no associated contact
Halt workflow immediately. Log deal_id and missing association. Send alert email to GMAIL_SENDER_ADDRESS with deal ID for manual review. Do not proceed to welcome email step.
HubSpot (read)
GET contact or deal returns 429 or 5xx
Retry up to 3 times with exponential backoff (2s, 4s, 8s). If all retries fail, halt Agent 1 run, log failure, and notify operations owner. Queue deal_id for manual restart.
Gmail (send welcome email)
OAuth token expired or invalid scope
Attempt token refresh using GMAIL_OAUTH_REFRESH_TOKEN. If refresh fails, halt step, log OAuth error, and alert operations owner to re-authorise Gmail connection. Do not skip welcome email; it is a mandatory step.
Gmail (send welcome email)
Send returns 4xx (invalid recipient address)
Log failed send with deal_id and recipient email. Notify operations owner with the invalid address for correction in HubSpot. Do not proceed to Typeform dispatch until email is confirmed deliverable.
Gmail (48h reminder)
Reminder timer fires but intake form already submitted
Cancel reminder step. Log that submission was detected before reminder window elapsed. No email sent. This is expected behaviour and not an error; log as info.
Typeform (webhook)
Webhook delivery fails or is not received within 48 hours of form dispatch
After 48-hour window, trigger the reminder email via Gmail. After a second 48-hour window with no submission, log as stalled onboarding and alert operations owner for manual follow-up. Do not block Xero or Slack steps indefinitely.
Typeform (webhook)
Duplicate submission received for same response_id
Check orchestration data store for existing response_id. If already processed, discard duplicate payload and log as deduplication event. Do not re-run downstream steps.
Xero (invoice creation)
Billing email not present in intake form response
Halt Xero step. Log missing field. Notify operations owner and finance contact (GMAIL_SENDER_ADDRESS) with deal_id. Create a manual task flag in orchestration run log. Do not create a partial invoice.
Xero (invoice creation)
OAuth refresh token expired (tokens expire after 60 days of inactivity)
Log token expiry error. Alert operations owner to re-authorise Xero OAuth connection via XERO_CLIENT_ID flow. Halt invoice step; queue for manual creation until re-authorised.
Xero (invoice creation)
Contact lookup returns duplicate contacts for same billing email
Use the first matching contact returned by the API. Log the duplicate contact condition with both ContactID values and alert the finance contact for manual deduplication in Xero. Proceed with invoice creation to avoid blocking onboarding.
Slack (notification)
Bot not in channel or channel_not_found error
Log Slack error with channel ID. Retry once after 30 seconds. If second attempt fails, fall back to sending the new-client notification as an email to the operations team mailing list. Do not silently drop the notification.
Unhandled exceptions (any error not matched by the scenarios above) must not fail silently. The orchestration platform must catch all unhandled exceptions, log the full error payload including step name, run_id, and deal_id, and send an alert to support@gofullspec.com and the process owner. Every failed run must be available for manual replay once the underlying issue is resolved.
Integration and API SpecPage 3 of 3