Back to VIP Customer Management

Integration and API Spec

The reference during the build: every tool connection, auth scope, field mapping, and error-handling rule.

4 pagesPDF · Technical
FS-DOC-05Technical

Integration and API Spec

VIP Customer Management

[YourCompany.com] · Customer Support Department · Prepared by FullSpec · [Today's Date]

This document defines every integration point, API credential, field mapping, and error-handling behaviour required to build and operate the VIP Customer Management automation. It is written for the FullSpec build team and covers the six tools in the confirmed stack: Xero, HubSpot, Gmail, Zendesk, Slack, and Typeform. Each section provides the exact configuration needed to implement, test, and maintain the three agents without ambiguity. All credentials must be stored in the shared credential store; nothing is hardcoded into workflow logic.

01Tool inventory

Tool
Role in automation
Auth method
Min plan required
Used by agents
Orchestration layer
Hosts all three workflows, credential store, and retry logic
Internal service account
N/A (build platform)
All agents
Xero
Source of customer cumulative spend data for tier calculation
OAuth 2.0 (PKCE)
Starter or above
Agent 1 (VIP Tier Agent)
HubSpot
CRM record store; tier property updates, sequence enrolment, timeline logging
OAuth 2.0 / Private App token
Sales Hub Starter or above
Agents 1, 2, 3
Gmail
Outbound delivery of personalised tier-up and check-in emails
OAuth 2.0 (Google Identity)
Google Workspace (any tier)
Agent 2 (VIP Outreach Agent)
Zendesk
Ticket priority tagging for VIP-flagged contacts
API token + email (Basic) or OAuth 2.0
Support Team or above
Agent 3 (VIP Support Escalation Agent)
Slack
Internal escalation alerts to assigned account managers
OAuth 2.0 (Bot token)
Free or above
Agent 3 (VIP Support Escalation Agent)
Typeform
VIP satisfaction survey dispatch and low-score webhook responses
Personal Access Token / OAuth 2.0
Basic or above (webhook required: Plus+)
Agents 2, 3
Before you connect anything: all integrations must be validated against sandbox or test environments before production credentials are entered into the credential store. Use Xero's demo company, HubSpot's sandbox portal, Zendesk's trial subdomain, and Typeform's test workspace. Do not point any workflow trigger at live customer data until end-to-end QA is signed off.
Integration and API SpecPage 1 of 4
FS-DOC-05Technical

02Per-tool integration detail

Xero

Xero is the spend data source for the VIP Tier Agent. The integration reads invoice and payment records to calculate each contact's cumulative spend and compare it against tier thresholds defined in the credential store.

Auth method
OAuth 2.0 with PKCE. Register a Xero app at developer.xero.com. Store client_id and client_secret in the credential store. Refresh tokens are long-lived; the platform must handle token refresh automatically before expiry (currently 60 days).
Required scopes
openid profile email accounting.transactions.read accounting.contacts.read offline_access
Trigger setup
Polled trigger. The VIP Tier Agent polls the Xero Invoices endpoint every 15 minutes for invoices with Status=AUTHORISED or Status=PAID updated since the last poll timestamp. Polling interval is configurable in the credential store as XERO_POLL_INTERVAL_MINUTES.
Required configuration
XERO_TENANT_ID: stored in credential store. VIP_TIER_BRONZE_THRESHOLD, VIP_TIER_SILVER_THRESHOLD, VIP_TIER_GOLD_THRESHOLD: spend values in USD, stored in credential store. Customer matching key: ContactID from Xero mapped to hubspot_xero_contact_id custom property on HubSpot contact.
Rate limits
Xero enforces 60 API calls/minute per connection and 5,000 calls/day. At current volume (~40 VIP contacts, weekly full review, 15-minute delta poll) estimated daily call volume is under 300. Throttling is not required at current volume but the platform must honour 429 responses with exponential backoff.
Constraints
Xero does not push webhooks for invoice events on Starter plans. The polling approach is mandatory unless the tenant upgrades to a plan with webhook support. Multi-currency invoices must be normalised to USD using the exchange rate returned in the invoice payload (CurrencyRate field) before tier comparison.
// Input
GET /api.xro/2.0/Invoices?where=Status=="AUTHORISED"OR Status=="PAID"&modifiedAfter={last_poll_ts}
// Key fields extracted
Invoice.Contact.ContactID  -> xero_contact_id
Invoice.SubTotal           -> invoice_subtotal
Invoice.CurrencyRate       -> currency_rate
Invoice.DateString         -> invoice_date
// Output to Agent 1
{ xero_contact_id, cumulative_spend_usd, last_invoice_date }
HubSpot

HubSpot is the central CRM used by all three agents: Agent 1 reads and writes contact tier properties, Agent 2 manages sequence enrolment and timeline events, and Agent 3 reads VIP flag status when a Zendesk ticket arrives.

Auth method
Private App token (preferred over legacy API key). Create a Private App in HubSpot Settings > Integrations > Private Apps. Store the bearer token as HUBSPOT_PRIVATE_APP_TOKEN in the credential store. Do not use OAuth 2.0 for server-to-server calls in this build.
Required scopes
crm.objects.contacts.read crm.objects.contacts.write crm.objects.deals.read timeline crm.schemas.contacts.read crm.schemas.contacts.write marketing-email sequences
Webhook / trigger setup
Agent 1 writes to HubSpot after Xero poll. Agent 2 is triggered by a HubSpot contact property webhook: subscribe to contact.propertyChange for the property vip_tier. Configure in HubSpot Settings > Integrations > Webhooks. The platform must validate the X-HubSpot-Signature-v3 header using HMAC-SHA256 with HUBSPOT_WEBHOOK_SECRET from the credential store.
Required configuration
Custom contact properties to create before build: vip_tier (enumeration: none, bronze, silver, gold), vip_qualification_date (date), vip_tier_changed (boolean), hubspot_xero_contact_id (single-line text). Sequence IDs for bronze, silver, and gold check-in sequences stored as HUBSPOT_SEQ_BRONZE, HUBSPOT_SEQ_SILVER, HUBSPOT_SEQ_GOLD in the credential store. Owner ID for each account manager stored as HUBSPOT_OWNER_ID_{INITIALS}.
Rate limits
HubSpot allows 150 API calls/10 seconds and 250,000 calls/day on Sales Hub Starter. At current volume, peak load is approximately 40 contact reads plus 40 writes per review cycle plus webhook events. Well within limits. Throttling is not required at current volume.
Constraints
Sequence enrolment requires Sales Hub Starter or above and the enrolling user must be the contact owner. Verify owner assignment before enrolment calls. The timeline event API requires the appId registered with the Private App; store as HUBSPOT_APP_ID in the credential store.
// Input: contact lookup by xero_contact_id
GET /crm/v3/objects/contacts/search
  filter: hubspot_xero_contact_id == {xero_contact_id}
// Output: existing tier state
{ hs_object_id, vip_tier, vip_qualification_date, email, firstname, lastname, hubspot_owner_id }
// Input: tier update (PATCH)
PATCH /crm/v3/objects/contacts/{hs_object_id}
  body: { vip_tier, vip_qualification_date, vip_tier_changed }
// Input: sequence enrolment
POST /automation/v4/sequences/enrollments
  body: { sequenceId: HUBSPOT_SEQ_{TIER}, contactId: hs_object_id, senderEmail }
Gmail

Gmail delivers personalised tier-up and check-in emails generated by the VIP Outreach Agent. All emails are sent from the authorised account manager mailbox associated with the contact owner.

Auth method
OAuth 2.0 using Google Identity Platform. Each sending mailbox requires its own OAuth consent. Store refresh tokens per sender as GMAIL_REFRESH_TOKEN_{OWNER_INITIALS} in the credential store. Client credentials stored 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 from Gmail in this build. Gmail is an action-only integration triggered by Agent 2 after a HubSpot vip_tier_changed event.
Required configuration
Email template IDs stored in the credential store: GMAIL_TEMPLATE_TIER_UP_BRONZE, GMAIL_TEMPLATE_TIER_UP_SILVER, GMAIL_TEMPLATE_TIER_UP_GOLD, GMAIL_TEMPLATE_CHECKIN_30, GMAIL_TEMPLATE_CHECKIN_60, GMAIL_TEMPLATE_CHECKIN_90. Templates must include the placeholders {{first_name}}, {{tier_label}}, {{account_manager_name}}, {{benefit_summary}}. Templates are maintained in a Google Doc and rendered to MIME at send time; the Doc IDs are stored as GMAIL_TEMPLATE_DOC_ID_{KEY}.
Rate limits
Gmail API allows 250 quota units/second and 1,000,000 units/day. A send request costs 100 units. At current volume (~40 VIP contacts, max 3 emails/contact/quarter) peak is well under 200 send calls/day. Throttling is not required at current volume.
Constraints
Emails must be sent from the contact owner's authenticated mailbox, not a shared alias, to preserve reply threading. If a contact has no assigned owner, the platform must route to a fallback sender stored as GMAIL_FALLBACK_SENDER in the credential store and log a warning.
// Input from Agent 2
{ recipient_email, first_name, tier_label, account_manager_name,
  benefit_summary, template_key, sender_refresh_token }
// Action
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
  body: RFC 2822 MIME message encoded as base64url
// Output
{ messageId, threadId } -> logged to HubSpot timeline as engagement
Zendesk

Zendesk is monitored by the VIP Support Escalation Agent. When a new ticket arrives, the agent checks whether the submitter is a VIP-flagged HubSpot contact and applies a priority tag if so.

Auth method
OAuth 2.0 preferred. Register an OAuth client in Zendesk Admin > Apps and Integrations > OAuth Clients. Store the bearer token as ZENDESK_OAUTH_TOKEN in the credential store. Alternatively, API token with email can be used for simpler setups; store as ZENDESK_API_TOKEN and ZENDESK_AGENT_EMAIL.
Required scopes
tickets:read tickets:write users:read
Webhook / trigger setup
Zendesk webhook (push). Create a Zendesk Trigger in Admin > Objects and Rules > Triggers: condition is Ticket created, action is Notify active webhook. The webhook endpoint is the platform's inbound URL for Agent 3. Validate the webhook signature using the Zendesk-provided signing secret stored as ZENDESK_WEBHOOK_SECRET; the signature is sent in the X-Zendesk-Webhook-Signature header using HMAC-SHA256.
Required configuration
ZENDESK_SUBDOMAIN: stored in credential store (e.g. yourcompany). VIP priority tag string stored as ZENDESK_VIP_TAG (e.g. vip_priority). Zendesk Trigger ID stored as ZENDESK_TRIGGER_ID for audit reference. Email-to-user lookup uses the requester email from the ticket payload matched against HubSpot contact email.
Rate limits
Zendesk Support Team allows 700 API calls/minute. At current volume (~40 VIP tickets/month, plus tag update calls) estimated peak is under 10 calls/minute. Throttling is not required at current volume.
Constraints
Zendesk triggers fire synchronously at ticket creation. The platform must respond to the webhook within 5 seconds or Zendesk will mark the delivery as failed and retry. Keep the tag-update logic lightweight; defer HubSpot lookups to an async sub-step if latency is a concern.
// Inbound webhook payload (selected fields)
{ ticket.id, ticket.subject, ticket.requester.email,
  ticket.requester.name, ticket.created_at, ticket.status }
// Lookup: is requester a VIP contact?
HubSpot contact search by email -> vip_tier != 'none'
// Action: apply tag
PUT /api/v2/tickets/{ticket.id}
  body: { ticket: { tags: [...existing_tags, ZENDESK_VIP_TAG],
                    priority: 'high' } }
// Output to Agent 3
{ ticket_id, ticket_url, requester_email, vip_tier, hubspot_contact_url }
Slack

Slack is the internal alert channel for the VIP Support Escalation Agent. It delivers real-time notifications to assigned account managers when a VIP ticket is created or a survey response falls below the alert threshold.

Auth method
OAuth 2.0 Bot token. Install the Slack app into the workspace and store the bot token as SLACK_BOT_TOKEN in the credential store. Do not use legacy incoming webhooks; use the chat.postMessage API method for structured block messages.
Required scopes
chat:write chat:write.public users:read users:read.email channels:read
Webhook / trigger setup
Slack is an action-only integration in this build. No inbound events from Slack are consumed. Messages are posted by Agent 3 in response to Zendesk ticket events and Typeform low-score webhook events.
Required configuration
Target channel IDs stored in the credential store: SLACK_CHANNEL_VIP_ESCALATIONS (for ticket alerts), SLACK_CHANNEL_SURVEY_ALERTS (for low-score alerts). Account manager Slack user IDs stored as SLACK_USER_ID_{OWNER_INITIALS} to enable @mentions in messages. Message templates use Block Kit JSON; template payloads stored as SLACK_BLOCK_TICKET_ALERT and SLACK_BLOCK_SURVEY_ALERT in the credential store.
Rate limits
Slack allows 1 message/second per channel by default (Tier 3: up to 50 burst). At current volume the automation posts fewer than 5 messages/day on average. Throttling is not required at current volume.
Constraints
PII must not be included in Slack message bodies beyond first name, VIP tier label, and hyperlinked ticket or HubSpot URLs. Do not include spend amounts, contact email addresses, or financial data in Slack payloads. This aligns with the security compliance requirements for this process.
// Input from Agent 3 (ticket alert)
{ contact_first_name, vip_tier, ticket_id, ticket_url,
  hubspot_contact_url, account_manager_slack_user_id }
// Action
POST https://slack.com/api/chat.postMessage
  body: { channel: SLACK_CHANNEL_VIP_ESCALATIONS,
          blocks: [ ...SLACK_BLOCK_TICKET_ALERT rendered with above fields ] }
// Input from Agent 3 (survey alert)
{ contact_first_name, vip_tier, survey_score, typeform_response_url,
  hubspot_contact_url, account_manager_slack_user_id }
// Action
POST https://slack.com/api/chat.postMessage
  body: { channel: SLACK_CHANNEL_SURVEY_ALERTS,
          blocks: [ ...SLACK_BLOCK_SURVEY_ALERT rendered with above fields ] }
Typeform

Typeform dispatches quarterly VIP satisfaction surveys triggered by Agent 2 at the 90-day mark of a contact's VIP enrolment. Survey response webhooks are consumed by Agent 3 to detect low scores and fire Slack alerts.

Auth method
Personal Access Token for server-side API calls. Store as TYPEFORM_ACCESS_TOKEN in the credential store. For OAuth 2.0 app flows, also store TYPEFORM_CLIENT_ID and TYPEFORM_CLIENT_SECRET. Webhook secret for inbound payload validation stored as TYPEFORM_WEBHOOK_SECRET.
Required scopes
responses:read forms:read webhooks:write webhooks:read (OAuth scopes); equivalent permissions granted automatically to Personal Access Tokens with full access.
Webhook / trigger setup
Inbound webhook from Typeform to Agent 3. Register the webhook via POST /forms/{form_id}/webhooks with the platform's inbound URL. Typeform signs payloads using HMAC-SHA256; validate against TYPEFORM_WEBHOOK_SECRET in the Typeform-Signature header. Outbound survey dispatch uses the Typeform Responses API to create a prefilled link per contact; the form ID is stored as TYPEFORM_VIP_SURVEY_FORM_ID.
Required configuration
TYPEFORM_VIP_SURVEY_FORM_ID: the form ID of the VIP satisfaction survey, stored in the credential store. Hidden fields in the Typeform form: hubspot_contact_id, contact_email, vip_tier (used to tag responses for routing). Low-score threshold stored as TYPEFORM_ALERT_SCORE_THRESHOLD (integer, e.g. 6 on a 0-10 scale); calibrate with the support manager before launch. TYPEFORM_WEBHOOK_ID: stored after registration for management reference.
Rate limits
Typeform API allows 60 requests/minute on Plus and above. At current volume (40 surveys/quarter, plus webhook events) API usage is negligible. Throttling is not required at current volume.
Constraints
Prefilled survey links require hidden fields to be enabled on the form before build. Verify this in the Typeform form settings. Webhook delivery is not guaranteed; the platform must implement idempotency using the Typeform response token field to avoid duplicate Slack alerts if the webhook fires more than once for the same response.
// Outbound: generate prefilled survey link (Agent 2)
GET /forms/{TYPEFORM_VIP_SURVEY_FORM_ID}
  -> construct prefilled URL with hidden fields:
     ?hubspot_contact_id={hs_object_id}&contact_email={email}&vip_tier={vip_tier}
// Log send event on HubSpot timeline
POST /crm/v3/timeline/events  body: { eventTemplateId, objectId, tokens }

// Inbound webhook payload (Agent 3)
{ form_response.token, form_response.submitted_at,
  form_response.hidden.hubspot_contact_id,
  form_response.hidden.vip_tier,
  form_response.answers[].field.ref == 'satisfaction_score' .number }
// Routing logic
if satisfaction_score < TYPEFORM_ALERT_SCORE_THRESHOLD -> fire Slack survey alert
else                                                    -> log response, no alert
Integration and API SpecPage 2 of 4
FS-DOC-05Technical

03Field mappings between tools

The following tables document every field that crosses a tool boundary during an agent handoff. Field names are shown in their exact API or property key format as they appear in each system's payload.

Agent 1 handoff: Xero to HubSpot (VIP Tier Agent)

Source tool
Source field
Destination tool
Destination field
Xero
Invoice.Contact.ContactID
HubSpot
hubspot_xero_contact_id
Xero
Invoice.SubTotal * Invoice.CurrencyRate (sum per contact)
HubSpot
vip_cumulative_spend_usd (custom)
Xero
Invoice.DateString (most recent)
HubSpot
vip_last_transaction_date (custom)
Xero
(derived: tier based on threshold comparison)
HubSpot
vip_tier (enumeration: none|bronze|silver|gold)
Xero
(derived: boolean, previous vs new tier)
HubSpot
vip_tier_changed (boolean)
Xero
(derived: ISO date at time of update)
HubSpot
vip_qualification_date (date)

Agent 2 handoff: HubSpot to Gmail (VIP Outreach Agent, tier-up email)

Source tool
Source field
Destination tool
Destination field
HubSpot
contact.properties.firstname
Gmail
{{first_name}} in template body
HubSpot
contact.properties.lastname
Gmail
{{last_name}} in template body
HubSpot
contact.properties.email
Gmail
To: header (recipient address)
HubSpot
contact.properties.vip_tier
Gmail
{{tier_label}} in template subject and body
HubSpot
contact.properties.hubspot_owner_id -> owner.email
Gmail
From: header (sender address, OAuth identity)
HubSpot
contact.properties.hubspot_owner_id -> owner.firstname
Gmail
{{account_manager_name}} in template signature
HubSpot
(derived from vip_tier: benefit copy block)
Gmail
{{benefit_summary}} in template body
Gmail
message.id (returned after send)
HubSpot
timeline engagement externalId

Agent 2 handoff: HubSpot to Typeform (VIP Outreach Agent, survey dispatch)

Source tool
Source field
Destination tool
Destination field
HubSpot
contact.properties.hs_object_id
Typeform
hidden field: hubspot_contact_id
HubSpot
contact.properties.email
Typeform
hidden field: contact_email
HubSpot
contact.properties.vip_tier
Typeform
hidden field: vip_tier
HubSpot
contact.properties.firstname
Typeform
prefilled field: respondent_first_name (if form supports it)

Agent 3 handoff: Zendesk to HubSpot and Slack (VIP Support Escalation Agent, ticket event)

Source tool
Source field
Destination tool
Destination field
Zendesk
ticket.requester.email
HubSpot
contact search filter: email == requester.email
HubSpot
contact.properties.vip_tier
Zendesk
(gate: proceed only if vip_tier != 'none')
Zendesk
ticket.id
Zendesk
tags array: append ZENDESK_VIP_TAG; priority: 'high'
Zendesk
ticket.id + ZENDESK_SUBDOMAIN
Slack
ticket_url in Block Kit message
HubSpot
contact.properties.hs_object_id + portal_id
Slack
hubspot_contact_url in Block Kit message
HubSpot
contact.properties.firstname
Slack
{{contact_first_name}} in Block Kit message
HubSpot
contact.properties.vip_tier
Slack
{{vip_tier}} in Block Kit message
HubSpot
contact.properties.hubspot_owner_id -> Slack user ID lookup
Slack
@mention in Block Kit message body

Agent 3 handoff: Typeform to Slack (VIP Support Escalation Agent, low-score survey alert)

Source tool
Source field
Destination tool
Destination field
Typeform
form_response.hidden.hubspot_contact_id
HubSpot
contact lookup: hs_object_id
Typeform
form_response.hidden.vip_tier
Slack
{{vip_tier}} in Block Kit survey alert
Typeform
form_response.answers[ref=satisfaction_score].number
Slack
{{survey_score}} in Block Kit survey alert
Typeform
form_response.token
Platform
idempotency key: deduplicate webhook deliveries
HubSpot
contact.properties.firstname
Slack
{{contact_first_name}} in Block Kit survey alert
HubSpot
contact.properties.hubspot_owner_id -> Slack user ID
Slack
@mention in Block Kit survey alert
Integration and API SpecPage 3 of 4
FS-DOC-05Technical

04Build stack and orchestration

Orchestration layout
Three discrete workflows, one per agent. Each workflow is independently deployable and versioned. A shared credential store is mounted into all three workflows at runtime. No credentials are hardcoded in workflow logic.
Workflow 1: VIP Tier Agent
Polled trigger on a 15-minute interval against the Xero Invoices endpoint. On each poll, the workflow processes new or updated invoices, calculates cumulative spend per contact, compares against tier thresholds, and patches the HubSpot contact record if tier has changed. A secondary scheduled trigger (weekly, configurable) performs a full-review pass across all contacts to catch any drift.
Workflow 2: VIP Outreach Agent
Webhook trigger: receives the HubSpot vip_tier property change webhook. Validates the X-HubSpot-Signature-v3 header. On valid payload, determines whether a tier-up email is required, sends via Gmail, enrolls the contact in the appropriate HubSpot sequence, and at the 90-day sequence milestone dispatches the Typeform survey link. A scheduled sub-trigger manages the 30, 60, and 90-day check-in cadence for contacts already enrolled.
Workflow 3: VIP Support Escalation Agent
Dual trigger: (a) inbound Zendesk webhook on ticket creation, validated via X-Zendesk-Webhook-Signature; (b) inbound Typeform webhook on form response submission, validated via Typeform-Signature header. Each trigger path runs independently through the same HubSpot contact lookup and Slack alert logic.
Credential store scope
All tokens, IDs, thresholds, and template references are stored in the shared credential store. No value that requires rotation or environment-specific configuration appears in workflow code. Each workflow accesses only the credential keys it requires; the store is partitioned by workflow for least-privilege access.
Signature validation
All inbound webhooks (HubSpot, Zendesk, Typeform) must validate the signature header on every request before any business logic executes. Requests failing validation must return HTTP 401 and halt. This check is the first step in each webhook-triggered workflow branch.
Environment separation
Two credential store environments: staging and production. Staging credentials point to sandbox or demo instances of each tool. Promotion from staging to production requires a manual approval step by the FullSpec build lead after QA sign-off.

Credential store contents (all values environment-scoped to staging and production):

Shared credential store: all keys required across the three agent workflows
// Xero
XERO_CLIENT_ID                  = <OAuth app client ID>
XERO_CLIENT_SECRET              = <OAuth app client secret>
XERO_TENANT_ID                  = <Xero organisation tenant UUID>
XERO_REFRESH_TOKEN              = <current refresh token, auto-rotated>
XERO_POLL_INTERVAL_MINUTES      = 15

// VIP tier thresholds (USD cumulative spend)
VIP_TIER_BRONZE_THRESHOLD       = <integer, e.g. 1000>
VIP_TIER_SILVER_THRESHOLD       = <integer, e.g. 5000>
VIP_TIER_GOLD_THRESHOLD         = <integer, e.g. 15000>

// HubSpot
HUBSPOT_PRIVATE_APP_TOKEN       = <bearer token>
HUBSPOT_WEBHOOK_SECRET          = <HMAC signing secret from HubSpot app>
HUBSPOT_APP_ID                  = <numeric app ID for timeline events>
HUBSPOT_PORTAL_ID               = <HubSpot portal/account ID>
HUBSPOT_SEQ_BRONZE              = <sequence ID integer>
HUBSPOT_SEQ_SILVER              = <sequence ID integer>
HUBSPOT_SEQ_GOLD                = <sequence ID integer>
HUBSPOT_OWNER_ID_RO             = <HubSpot owner ID for Rachel Oduya>
HUBSPOT_OWNER_ID_TF             = <HubSpot owner ID for Tom Fielding>
HUBSPOT_OWNER_ID_PS             = <HubSpot owner ID for Priya Shan>

// Gmail
GMAIL_CLIENT_ID                 = <Google OAuth client ID>
GMAIL_CLIENT_SECRET             = <Google OAuth client secret>
GMAIL_REFRESH_TOKEN_RO          = <refresh token for Rachel Oduya mailbox>
GMAIL_REFRESH_TOKEN_TF          = <refresh token for Tom Fielding mailbox>
GMAIL_FALLBACK_SENDER           = <fallback sender email address>
GMAIL_TEMPLATE_TIER_UP_BRONZE   = <Google Doc ID>
GMAIL_TEMPLATE_TIER_UP_SILVER   = <Google Doc ID>
GMAIL_TEMPLATE_TIER_UP_GOLD     = <Google Doc ID>
GMAIL_TEMPLATE_CHECKIN_30       = <Google Doc ID>
GMAIL_TEMPLATE_CHECKIN_60       = <Google Doc ID>
GMAIL_TEMPLATE_CHECKIN_90       = <Google Doc ID>

// Zendesk
ZENDESK_SUBDOMAIN               = <subdomain string, e.g. yourcompany>
ZENDESK_OAUTH_TOKEN             = <bearer token>
ZENDESK_WEBHOOK_SECRET          = <HMAC signing secret from Zendesk>
ZENDESK_TRIGGER_ID              = <numeric trigger ID>
ZENDESK_VIP_TAG                 = vip_priority

// Slack
SLACK_BOT_TOKEN                 = <xoxb- prefixed bot token>
SLACK_CHANNEL_VIP_ESCALATIONS   = <channel ID, e.g. C0XXXXXXX>
SLACK_CHANNEL_SURVEY_ALERTS     = <channel ID>
SLACK_USER_ID_RO                = <Slack user ID for Rachel Oduya>
SLACK_USER_ID_TF                = <Slack user ID for Tom Fielding>
SLACK_USER_ID_PS                = <Slack user ID for Priya Shan>
SLACK_BLOCK_TICKET_ALERT        = <Block Kit JSON template string>
SLACK_BLOCK_SURVEY_ALERT        = <Block Kit JSON template string>

// Typeform
TYPEFORM_ACCESS_TOKEN           = <personal access token>
TYPEFORM_CLIENT_ID              = <OAuth app client ID>
TYPEFORM_CLIENT_SECRET          = <OAuth app client secret>
TYPEFORM_WEBHOOK_SECRET         = <HMAC signing secret>
TYPEFORM_VIP_SURVEY_FORM_ID     = <form ID string>
TYPEFORM_WEBHOOK_ID             = <registered webhook ID>
TYPEFORM_ALERT_SCORE_THRESHOLD  = 6

05Error handling and retry logic

Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently. All errors must be logged to the platform's execution log with the workflow name, step name, timestamp, HTTP status code or exception type, and the payload reference (contact ID or ticket ID where available). Critical failures that block a VIP action must post a fallback alert to SLACK_CHANNEL_VIP_ESCALATIONS.

Integration
Scenario
Required behaviour
Xero: poll request
HTTP 429 rate limit response
Pause poll; retry after Retry-After header value (or 60 s if absent). Apply exponential backoff: 1 min, 2 min, 4 min up to 3 retries. If all retries fail, log error and skip cycle; resume on next scheduled poll interval. Do not alert Slack for a single failed poll.
Xero: poll request
HTTP 401 or 403 (token expired or revoked)
Attempt token refresh using stored refresh token. If refresh succeeds, retry the original request once. If refresh fails, halt the workflow, log a critical error, and post to SLACK_CHANNEL_VIP_ESCALATIONS with the message 'Xero auth failure: manual token re-authorisation required'. Do not retry further until credentials are updated.
Xero to HubSpot: contact match
No HubSpot contact found for xero_contact_id
Log a warning with the unmatched xero_contact_id. Skip the tier update for that contact. Add the contact ID to a daily unmatched-contacts log accessible to the FullSpec team. Do not halt the workflow; continue processing remaining contacts in the batch.
HubSpot: contact PATCH
HTTP 429 or 503 transient error
Retry with exponential backoff: 10 s, 30 s, 90 s (3 attempts). If all retries fail, queue the update for retry on the next poll cycle using the platform's built-in retry queue. Log the failure with the hs_object_id and intended property values.
HubSpot: sequence enrolment
Contact already enrolled in the target sequence
HubSpot returns a 409 or equivalent conflict. Catch this response, log it as informational (not an error), and skip the enrolment call. Do not re-enrol the contact. Continue with remaining workflow steps.
HubSpot webhook: inbound vip_tier change
Invalid or missing X-HubSpot-Signature-v3 header
Return HTTP 401 immediately. Log the rejected request with the source IP and timestamp. Do not process the payload. Alert the FullSpec team if more than 5 invalid requests are received within 10 minutes (potential replay attack).
Gmail: send request
HTTP 429 quota exceeded
Retry after 60 s; exponential backoff up to 3 retries (60 s, 120 s, 240 s). If all retries fail, log the failure with the recipient email and template key, and post a fallback alert to SLACK_CHANNEL_VIP_ESCALATIONS: 'Email send failed for [contact first name]: manual send required'. Do not lose the email data.
Gmail: OAuth token
Refresh token revoked or expired for a sender mailbox
Switch to GMAIL_FALLBACK_SENDER for the affected contact's email. Log a critical error identifying the affected GMAIL_REFRESH_TOKEN_{INITIALS} key. Post to SLACK_CHANNEL_VIP_ESCALATIONS with re-authorisation instructions. Continue sending from the fallback sender until credentials are restored.
Zendesk webhook: inbound ticket event
Platform does not respond within 5 s (Zendesk timeout)
Zendesk will retry delivery up to 3 times (at 1 min, 10 min, 30 min). The platform must be idempotent on ticket tag updates: check whether ZENDESK_VIP_TAG is already present before applying it to avoid duplicate actions on retried deliveries. Log each received webhook with the ticket.id.
Zendesk: ticket PATCH (tag update)
HTTP 422 or ticket not found
Log the error with the ticket_id. Skip the tag update. Post a fallback Slack message to SLACK_CHANNEL_VIP_ESCALATIONS: 'VIP tag could not be applied to Zendesk ticket [ticket_id]: manual tagging required'. Do not halt the Slack alert step; proceed with the account manager notification regardless.
Typeform webhook: inbound response
Duplicate webhook delivery (same form_response.token)
Check the form_response.token against the platform's idempotency log before processing. If the token has already been processed within the last 24 hours, return HTTP 200 and discard the payload without triggering a Slack alert. Log the duplicate for audit purposes.
Slack: chat.postMessage
HTTP 429 rate limit or channel not found
Retry after 1 s for rate limit (Slack sends Retry-After header). If the channel ID is invalid, log a critical error and attempt to post to the fallback channel SLACK_CHANNEL_VIP_ESCALATIONS. If that also fails, write the alert content to the platform execution log as a CRITICAL entry. Unhandled alert failures must never fail silently under any circumstances.
All error states that prevent a VIP customer action (failed email send, failed ticket tag, failed Slack alert) must produce a human-readable fallback notification through at least one remaining channel. If all channels are unavailable, the failure must be written to the platform execution log at CRITICAL severity and surfaced in the next daily log review. No VIP action may be silently dropped.

For support or questions about this specification, contact the FullSpec team at support@gofullspec.com.

Integration and API SpecPage 4 of 4

More documents for this process

Every document generated for VIP Customer Management.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Developer Handover Pack
Technical · Developer
View
Test and QA Plan
Quality · Developer
View