Developer Handover Pack
Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.
Developer Handover Pack
Complaint Escalation Workflow
[YourCompany.com] · Customer Support Department · Prepared by FullSpec · [Today's Date]
This document gives the FullSpec build team everything needed to implement the Complaint Escalation Workflow from a confirmed process map. It covers the current-state step breakdown, full agent specifications with IO contracts, an end-to-end data flow trace, and the recommended build stack. All numbered facts, volumes, and costs come directly from the confirmed process template. Where the template does not specify a value (OAuth scopes, rate limits, field names), FullSpec has supplied realistic, internally consistent specifics based on the named tools. Do not share this document with the client team without redacting the agent IO code blocks.
01Process snapshot
02Agent specifications
The Complaint Triage Agent fires the moment a new ticket is created in Zendesk from an incoming complaint. It reads the complaint body, runs sentiment analysis and keyword matching against a configurable severity ruleset, retrieves the customer's full record from HubSpot (purchase history, lifetime value, and prior complaint count), and produces a structured triage output: a severity score (low, medium, or high), a complaint category (billing, product, delivery, conduct, or other), and the enriched customer context block. All three outputs are written directly back to the Zendesk ticket as custom fields before the Escalation and Routing Agent fires. Estimated build time: 10 hours. Complexity: Moderate.
// Input zendesk.ticket.id : string // e.g. '10042' zendesk.ticket.subject : string // complaint subject line zendesk.ticket.description : string // full complaint body text zendesk.ticket.requester_email : string // customer email address zendesk.ticket.created_at : ISO8601 // timestamp of ticket creation // HubSpot lookup (by requester_email) hubspot.contact.id : string hubspot.contact.lifecycle_stage : string // e.g. 'customer' hubspot.contact.num_contacted_notes : integer // prior complaint count hubspot.contact.recent_deal_amount : number // last purchase value USD hubspot.contact.hs_lead_status : string // Output (written to Zendesk custom fields) triage.severity_score : enum['low','medium','high'] triage.complaint_category: enum['billing','product','delivery','conduct','other'] triage.sentiment_label : enum['negative','highly_negative','neutral'] triage.customer_tier : enum['standard','valued','vip'] // derived from deal amount triage.prior_complaints : integer // from HubSpot contact triage.hubspot_contact_id: string // appended for downstream lookups triage.triage_timestamp : ISO8601
- Zendesk tier requirement: Suite Team or above is required for custom ticket fields and webhook targets. Confirm the active plan before build begins. Professional tier is needed if SLA policy objects are to be read by the agent.
- HubSpot tier requirement: Free CRM tier is sufficient for contact GET requests. If deal pipeline data is required for customer tier scoring, a Starter tier or above must be confirmed.
- Severity scoring ruleset: The ruleset (keywords, sentiment thresholds, tier multipliers) must be agreed in writing with the support manager before the agent is built. Default thresholds are: any 'highly_negative' sentiment with 2+ prior complaints scores high; 'highly_negative' with 0-1 prior complaints scores medium; all other combinations score low.
- HubSpot lookup fallback: If no contact is found by email, the agent writes triage.customer_tier as 'standard' and triage.prior_complaints as 0, then continues. It does not block the workflow. The Zendesk ticket must receive a flag field 'triage.hubspot_matched: false' in this case.
- Dedupe: Zendesk ticket IDs are globally unique. No additional dedupe is required at this layer. Duplicate complaints submitted by the same customer within 10 minutes should be detected by checking for an open ticket with the same requester_email and a created_at within a 600-second window. If found, append a note to the existing ticket rather than creating a triage output for the duplicate.
- Custom field setup: Four Zendesk custom fields must be created before build: 'Severity Score' (dropdown), 'Complaint Category' (dropdown), 'Customer Tier' (dropdown), and 'Prior Complaints' (integer). Field IDs must be captured and added to the platform credential store before the agent node is wired.
- Confirm before build: Severity scoring criteria signed off by Priya Sandhu (Support Manager), HubSpot API key scoped to contacts.read and crm.objects.deals.read, and Zendesk webhook target URL registered on the active subdomain.
The Escalation and Routing Agent fires after the Complaint Triage Agent has written its output fields to the Zendesk ticket. It reads the severity score and complaint category, assigns the ticket to the correct agent or team queue in Zendesk using a configurable routing matrix, sends a Slack notification to the assigned agent with a direct ticket link and complaint summary, and, for high-severity cases only, sends a separate Slack alert to the support manager. It immediately sends a personalised acknowledgement email to the customer via Gmail, selecting the correct template variant based on severity tier (low, medium, or high). It then sets a time-based escalation check: if the ticket status has not moved to 'solved' or 'pending' by the agreed deadline, the agent fires an overdue alert to the manager's Slack channel and updates the ticket priority to 'urgent' in Zendesk. Estimated build time: 14 hours. Complexity: Complex.
// Input (from Zendesk ticket fields written by Triage Agent) zendesk.ticket.id : string triage.severity_score : enum['low','medium','high'] triage.complaint_category : enum['billing','product','delivery','conduct','other'] triage.customer_tier : enum['standard','valued','vip'] triage.prior_complaints : integer triage.hubspot_contact_id : string zendesk.ticket.requester_email : string zendesk.ticket.requester_name : string zendesk.ticket.created_at : ISO8601 // Routing matrix lookup (stored in platform credential / config store) routing.matrix[category][severity] -> zendesk.group_id : string routing.matrix[category][severity] -> zendesk.assignee_id : string // Output: Zendesk updates zendesk.ticket.group_id : string // assigned group zendesk.ticket.assignee_id : string // assigned agent zendesk.ticket.priority : enum['low','normal','high','urgent'] zendesk.ticket.status : 'open' // Output: Slack agent notification slack.channel : string // agent's direct Slack channel ID slack.message : string // ticket link, severity, category, customer name // Output: Slack manager alert (high-severity only) slack.channel : string // #support-escalations or manager DM slack.message : string // complaint summary, customer tier, prompt to review // Output: Gmail acknowledgement email gmail.to : string // requester_email gmail.subject : string // severity-tier template subject gmail.body_html : string // personalised template: agent name, ETA, ticket ref // Output: Overdue escalation (time-based, fires at deadline breach) slack.channel : string // manager channel slack.overdue_message : string // ticket ID, time overdue, current assignee zendesk.ticket.priority: 'urgent' // updated on breach
- Routing matrix: Must be built as a configurable lookup table (JSON object in the platform credential/config store), keyed by complaint_category and severity_score. Do not hard-code agent IDs. All Zendesk group_id and assignee_id values must be retrieved via the Zendesk Groups and Users API at build time and stored in the config table.
- Slack OAuth scopes required: chat:write, users:read, users:read.email. The bot must be invited to all target channels (#support-escalations and any agent DM threads) before go-live. Confirm channel IDs with the support manager before wiring.
- Gmail send account: A dedicated support@ sending address must be configured with OAuth2 (scope: gmail.send). Do not use a personal Gmail account. The account must have 'Send as' permissions confirmed for the sending domain.
- Gmail templates: Three template variants are required (low, medium, high severity). Templates must be approved by the support manager before build. Store template HTML in the platform config store, not in the workflow node itself, so they can be updated without a rebuild.
- Overdue escalation timing: Deadline values must be stored in the config table keyed by severity (e.g. high: 1 hour, medium: 4 hours, low: 24 hours). The time-based check should use the platform's built-in scheduler or a polling loop against zendesk.ticket.updated_at and zendesk.ticket.status. Confirm deadline values with Priya Sandhu before build.
- High-severity manager alert: The manager Slack message must include complaint summary (first 200 characters of ticket description), customer tier, prior complaint count, and a direct Zendesk ticket URL. Include a prompt string: 'Please review within 1 hour.'
- Escalation rate limit: Zendesk REST API rate limit is 700 requests per minute on Suite Team. At 90 complaints per month this is well within limits. Gmail API send limit is 2,000 messages per day for Workspace accounts. Both are safe at current volume.
- Confirm before build: Routing matrix agreed with support manager, Slack bot installed and channel IDs confirmed, Gmail OAuth2 credentials issued, overdue deadline values signed off, and all three acknowledgement email templates approved.
03End-to-end data flow
// ── TRIGGER ──────────────────────────────────────────────────────────────
// Event: Customer submits complaint via email or web form
// Source: Gmail inbound parse webhook OR web form POST to Zendesk API
inbound.from_email : string // customer email address
inbound.from_name : string // customer display name
inbound.subject : string // email subject or form title
inbound.body_text : string // plain-text complaint content
inbound.body_html : string // HTML version (if email)
inbound.attachments[] : file[] // any attachments
inbound.received_at : ISO8601 // UTC timestamp of receipt
// ── STEP 1: ZENDESK TICKET CREATION ─────────────────────────────────────
// Action: Automation platform calls Zendesk Tickets API (POST /api/v2/tickets)
zendesk.ticket.id <- generated by Zendesk
zendesk.ticket.subject <- inbound.subject
zendesk.ticket.description <- inbound.body_text
zendesk.ticket.requester_email <- inbound.from_email
zendesk.ticket.requester_name <- inbound.from_name
zendesk.ticket.status <- 'new'
zendesk.ticket.priority <- 'normal' // default, overridden by agent
zendesk.ticket.created_at <- inbound.received_at
zendesk.ticket.tags[] <- ['auto_intake']
// ── HANDOFF: Zendesk ticket.created event fires ───────────────────────
// Webhook payload delivered to automation platform inbound endpoint
// Complaint Triage Agent begins
// ── STEP 2: HUBSPOT CONTACT LOOKUP ───────────────────────────────────
// Action: GET /crm/v3/objects/contacts/search
// Filter: email eq zendesk.ticket.requester_email
hubspot.contact.id <- API response or null
hubspot.contact.lifecycle_stage <- API response or 'unknown'
hubspot.contact.num_contacted_notes <- API response or 0
hubspot.contact.recent_deal_amount <- API response or 0
// Derive customer tier from recent_deal_amount
triage.customer_tier <- IF recent_deal_amount >= 1000 THEN 'vip'
ELSE IF recent_deal_amount >= 200 THEN 'valued'
ELSE 'standard'
// Fallback: if no HubSpot contact found
triage.hubspot_matched <- false
triage.customer_tier <- 'standard'
triage.prior_complaints <- 0
// ── STEP 3: SEVERITY SCORING ─────────────────────────────────────────
// Action: Sentiment analysis on zendesk.ticket.description
// Keyword matching against configurable ruleset in config store
triage.sentiment_label <- enum['negative','highly_negative','neutral']
triage.keyword_flags[] <- matched severity keywords
// Scoring logic
triage.severity_score <- IF sentiment == 'highly_negative'
AND prior_complaints >= 2 THEN 'high'
ELSE IF sentiment == 'highly_negative' THEN 'medium'
ELSE 'low'
// Override: vip customer_tier always promotes severity one level up
triage.severity_score <- IF customer_tier == 'vip'
AND severity == 'low' THEN 'medium'
ELSE severity_score
triage.complaint_category <- enum['billing','product','delivery','conduct','other']
// Category assigned by keyword classifier in config ruleset
// ── STEP 4: WRITE TRIAGE OUTPUT TO ZENDESK ───────────────────────────
// Action: PUT /api/v2/tickets/{id}
// Update custom fields on Zendesk ticket
zendesk.ticket.custom_fields['severity_score'] <- triage.severity_score
zendesk.ticket.custom_fields['complaint_category'] <- triage.complaint_category
zendesk.ticket.custom_fields['customer_tier'] <- triage.customer_tier
zendesk.ticket.custom_fields['prior_complaints'] <- triage.prior_complaints
zendesk.ticket.custom_fields['hubspot_contact_id'] <- hubspot.contact.id
zendesk.ticket.custom_fields['triage_timestamp'] <- ISO8601 now()
triage.triage_timestamp <- ISO8601 now()
// ── HANDOFF: Triage Agent complete ───────────────────────────────────
// Escalation and Routing Agent begins
// ── STEP 5: TICKET ROUTING AND ASSIGNMENT ────────────────────────────
// Action: Lookup routing.matrix[complaint_category][severity_score]
// Returns zendesk.group_id and zendesk.assignee_id from config store
zendesk.ticket.group_id <- routing.matrix lookup result
zendesk.ticket.assignee_id <- routing.matrix lookup result
zendesk.ticket.priority <- IF severity == 'high' THEN 'high'
ELSE IF severity == 'medium' THEN 'normal'
ELSE 'low'
zendesk.ticket.status <- 'open'
// Action: PUT /api/v2/tickets/{id} to apply assignment
// ── STEP 6: SLACK AGENT NOTIFICATION ─────────────────────────────────
// Action: POST to Slack Web API chat.postMessage
slack.agent_dm.channel <- resolved from routing.matrix assignee Slack user ID
slack.agent_dm.text <- 'New {severity} complaint assigned: {ticket_url}'
slack.agent_dm.blocks[] <- [{type:'section', text: complaint_summary_200_chars},
{type:'section', text: 'Category: {category}'},
{type:'section', text: 'Customer tier: {tier}'},
{type:'actions', elements:[{ticket_url_button}]}]
// ── STEP 7: SLACK MANAGER ALERT (high-severity only) ─────────────────
// Condition: triage.severity_score == 'high'
slack.manager_alert.channel <- config.slack.manager_channel_id
// e.g. '#support-escalations' channel ID
slack.manager_alert.text <- 'HIGH severity complaint requires review'
slack.manager_alert.blocks[] <- [{complaint_summary_200_chars},
{customer_tier}, {prior_complaints},
{ticket_url_button},
{text:'Please review within 1 hour.'}]
// ── STEP 8: GMAIL ACKNOWLEDGEMENT EMAIL ──────────────────────────────
// Action: Gmail API POST /gmail/v1/users/me/messages/send
gmail.message.to <- zendesk.ticket.requester_email
gmail.message.from <- config.gmail.send_as_address
// e.g. 'support@[YourCompany.com]'
gmail.message.subject <- template[severity_score].subject
gmail.message.body_html <- template[severity_score].body_html
// Variables injected: {requester_name},
// {ticket_id}, {assignee_name}, {eta_string}
gmail.message.headers['X-ZD-Ticket-ID'] <- zendesk.ticket.id
// ── STEP 9: OVERDUE ESCALATION TIMER ─────────────────────────────────
// Action: Schedule time-based check from triage.triage_timestamp
// Deadline sourced from config: high=1hr, medium=4hr, low=24hr
timer.deadline <- triage.triage_timestamp + config.deadline[severity_score]
// At timer.deadline: poll Zendesk GET /api/v2/tickets/{id}
// Check: zendesk.ticket.status NOT IN ['solved','pending','closed']
// If overdue:
zendesk.ticket.priority <- 'urgent' // PATCH /api/v2/tickets/{id}
zendesk.ticket.tags[] <- append 'overdue_escalation'
slack.overdue_alert.channel <- config.slack.manager_channel_id
slack.overdue_alert.text <- 'OVERDUE: Ticket #{ticket_id} past deadline'
slack.overdue_alert.blocks[] <- [{ticket_id}, {time_overdue},
{current_assignee}, {ticket_url_button}]
// ── END OF AUTOMATED FLOW ────────────────────────────────────────────
// Remaining human step: Support agent resolves complaint and closes ticket
// Step 10: Agent updates zendesk.ticket.status <- 'solved'
// Optional: Notion resolution log entry written by agent manually04Recommended build stack
More documents for this process
Every document generated for Complaint Escalation Workflow.