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
Warranty & Guarantee Claim Handling
[YourCompany.com] · Customer Support Department · Prepared by FullSpec · [Today's Date]
This document gives the FullSpec build team everything needed to construct, connect, and validate the Warranty and Guarantee Claim Handling automation. It covers the current-state process map with bottlenecks identified, full agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. The process owner and support lead are responsible for providing accurate warranty rules, confirming escalation thresholds, and granting tool access before build begins. FullSpec handles all construction, wiring, and testing.
01Process snapshot
02Agent specifications
Monitors the designated Gmail inbox and the Zendesk form queue for new warranty claim submissions. On detection, the agent extracts structured claim data from the email body or form payload, creates a formatted Zendesk ticket with all relevant fields populated, and immediately sends an acknowledgement email to the claimant. This agent eliminates the manual ticket-creation and first-reply steps, ensuring every claim is logged and acknowledged within seconds of arrival regardless of staff availability.
// Input
gmail.message {
message_id: string,
from_email: string,
from_name: string,
subject: string,
body_plain: string,
received_at: ISO8601
}
// OR
zendesk.form_submission {
requester_email: string,
requester_name: string,
product_name: string,
order_number: string | null,
fault_description: string,
submitted_at: ISO8601
}
// Output
zendesk.ticket {
ticket_id: string,
status: 'open',
subject: 'Warranty Claim — {product_name}',
requester_email: string,
requester_name: string,
order_number: string | null,
fault_description: string,
claim_source: 'gmail' | 'zendesk_form',
created_at: ISO8601
}
gmail.sent_message {
to: requester_email,
subject: 'We have received your warranty claim — Ref #{ticket_id}',
body: acknowledgement_template,
sent_at: ISO8601
}- Gmail connection requires OAuth 2.0 with scopes gmail.readonly and gmail.send. The monitored inbox must use a dedicated label (e.g. 'warranty-claims') or a filter rule so the trigger does not fire on unrelated messages.
- Zendesk connection requires an API token scoped to ticket:write and ticket:read. The form queue webhook must be configured in Zendesk Admin under Settings > Integrations > Webhooks, pointing to the automation platform's inbound webhook URL.
- If a submission arrives via Gmail but already has a matching open Zendesk ticket (matched on requester_email within a 24-hour window), the agent must not create a duplicate. Implement a Zendesk search call before ticket creation and skip if a match is found.
- The acknowledgement email template must include the Zendesk ticket ID as a reference number. Confirm the template copy and sender address (e.g. support@[YourCompany.com]) with the support lead before build.
- If the email body does not contain a parseable order number, the ticket field order_number is set to null and a tag 'missing-order-number' is applied to the ticket so the Eligibility Agent can handle the fallback path.
- Must be confirmed before build: the Gmail label or filter rule, Zendesk subdomain and form ID, acknowledgement email template text, and sender address.
Triggered when the Claim Intake Agent creates a new Zendesk ticket, this agent queries Shopify to retrieve the matching order record, then compares the purchase date and product SKU against the warranty rules table in Google Sheets. It classifies the claim as auto-resolvable (within standard warranty terms and below the value threshold) or requiring manager approval (out-of-scope, high-value, or disputed). For claims requiring approval, it sends a structured Slack message to the designated approving manager. Regardless of path, it writes the claim status and resolution recommendation to the Google Sheets tracker row for that claim.
// Input
zendesk.ticket {
ticket_id: string,
requester_email: string,
order_number: string | null,
product_name: string,
fault_description: string
}
// Shopify lookup
shopify.order {
order_id: string,
customer_email: string,
line_items: [{ sku: string, product_title: string, price: decimal }],
created_at: ISO8601,
total_price: decimal,
financial_status: string
}
// Google Sheets warranty rules row
sheets.warranty_rule {
sku: string,
warranty_period_days: integer,
max_auto_resolve_value: decimal,
eligible_fault_types: string[],
default_resolution: 'replacement' | 'refund' | 'repair' | 'reject'
}
// Output — auto-resolve path
eligibility_result {
ticket_id: string,
order_id: string,
sku: string,
purchase_date: ISO8601,
warranty_expiry: ISO8601,
eligible: true,
resolution_type: 'replacement' | 'refund' | 'repair',
requires_approval: false,
order_total: decimal
}
// Output — approval path
eligibility_result {
ticket_id: string,
order_id: string,
sku: string,
purchase_date: ISO8601,
warranty_expiry: ISO8601,
eligible: boolean,
resolution_type: string,
requires_approval: true,
approval_reason: string,
order_total: decimal
}
slack.message_sent {
channel: '#warranty-approvals',
blocks: approval_block_kit_payload,
action_id: 'approve' | 'reject',
callback_claim_ref: ticket_id
}
// Google Sheets tracker row written
sheets.tracker_row {
claim_ref: ticket_id,
customer_email: string,
sku: string,
order_id: string,
status: 'pending_approval' | 'auto_approved' | 'ineligible',
resolution_type: string,
logged_at: ISO8601
}- Shopify requires a private app API key with read_orders scope. The primary lookup key is requester_email. If no order is found by email and order_number is present on the ticket, a secondary lookup by order_number must be attempted. If both fail, the ticket must be tagged 'order-not-found', the tracker row written with status 'manual-review', and the workflow halted without proceeding to resolution.
- Google Sheets must have two named ranges confirmed before build: 'WarrantyRules' (SKU-level rules table) and 'ClaimsTracker' (append-only log). The service account credential used by the automation platform must be granted Editor access to the sheet.
- The auto-resolve value threshold (max_auto_resolve_value per claim) and the list of out-of-scope fault types must be documented in the WarrantyRules sheet and confirmed with the operations manager before the eligibility logic is coded.
- Slack approval messages must use Block Kit interactive components (button actions) so the manager can approve or reject directly in Slack. The automation platform must expose a public inbound webhook to receive the Slack interactivity callback. The Slack app must be configured with Interactivity enabled and the Request URL set to that webhook.
- A timeout rule must be implemented: if no Slack response is received within 4 business hours, the claim is automatically escalated by posting a reminder to the same Slack channel and tagging the secondary approver. Confirm the secondary approver's Slack user ID before build.
- Eligibility logic: warranty_expiry = purchase_date + warranty_period_days. If today > warranty_expiry, the claim is ineligible. If order_total > max_auto_resolve_value, requires_approval is set to true regardless of eligibility.
- Must be confirmed before build: Shopify store URL and API credentials, Google Sheets document ID and named range structure, Slack workspace app credentials, approval channel name, approving manager Slack user ID, secondary approver Slack user ID, value threshold for auto-resolve, and the complete fault-type eligibility list.
Triggered either by a manager approval callback from Slack or by the auto-resolve flag set by the Eligibility and Routing Agent, this agent sends the final resolution message to the customer via Zendesk, including relevant dispatch or refund timeline details, and then closes the ticket. It does not process the Xero refund or warehouse dispatch, as those steps remain with human operators. Its sole responsibility is closing the customer-facing communication loop and updating the ticket status.
// Input — auto-resolve path
eligibility_result {
ticket_id: string,
requester_email: string,
resolution_type: 'replacement' | 'refund' | 'repair',
requires_approval: false
}
// Input — approval path
slack.callback {
action_id: 'approve',
callback_claim_ref: ticket_id,
approver_slack_user_id: string,
approved_at: ISO8601
}
// On approval
resolution_payload {
ticket_id: string,
resolution_type: string,
resolution_message: string, // populated from template by resolution_type
approved_by: string | 'auto',
resolved_at: ISO8601
}
// Output
zendesk.ticket_update {
ticket_id: string,
status: 'solved',
public_comment: resolution_message,
tags: ['warranty-resolved', resolution_type],
solved_at: ISO8601
}
sheets.tracker_row_update {
claim_ref: ticket_id,
status: 'resolved',
resolution_type: string,
resolved_at: ISO8601
}- Resolution message templates must be created for each resolution_type value (replacement, refund, repair) before build. Confirm the copy with the support lead. Each template should reference the ticket number and include realistic timelines (e.g. refund within 5 to 7 business days).
- If the Slack callback contains action 'reject', the agent must post a private Zendesk internal note explaining the rejection reason, leave the ticket open, and write 'rejected' to the tracker row. No customer-facing message is sent on rejection; the support lead handles that manually.
- Zendesk ticket closure uses the status value 'solved'. Confirm whether the Zendesk instance auto-moves 'solved' to 'closed' after a set period; if so, no additional step is needed.
- The Google Sheets tracker row must be updated to 'resolved' on ticket close so the live tracker remains accurate at every state change.
- Must be confirmed before build: resolution message templates for all three resolution types, Zendesk ticket 'solved' vs 'closed' behaviour, and the rejection handling procedure.
03End-to-end data flow
// ─────────────────────────────────────────────────────────────────
// TRIGGER
// ─────────────────────────────────────────────────────────────────
SOURCE: Gmail inbox (label: 'warranty-claims') OR Zendesk web form webhook
gmail.message -> {
message_id,
from_email, // used as primary customer identifier
from_name,
subject,
body_plain, // parsed for order_number, product_name, fault_description
received_at
}
zendesk.form_submission -> {
requester_email,
requester_name,
product_name,
order_number, // nullable; supplied by customer
fault_description,
submitted_at
}
// ─────────────────────────────────────────────────────────────────
// AGENT 1: Claim Intake Agent
// ─────────────────────────────────────────────────────────────────
STEP 1.1 — Dedupe check
zendesk.search(requester_email, status:'open', tag:'warranty-claim')
IF match found within 24h -> skip ticket creation, EXIT workflow
STEP 1.2 — Create Zendesk ticket
zendesk.tickets.create({
subject: 'Warranty Claim — {product_name}',
requester_email: from_email | requester_email,
requester_name: from_name | requester_name,
description: fault_description,
tags: ['warranty-claim'],
custom_fields: {
order_number: order_number | null,
claim_source: 'gmail' | 'zendesk_form',
product_name: product_name
}
})
-> ticket_id // passed to all downstream steps
STEP 1.3 — Send acknowledgement email
gmail.send({
to: requester_email,
subject: 'We have received your warranty claim — Ref #{ticket_id}',
body: acknowledgement_template(ticket_id, requester_name)
})
// HANDOFF: Claim Intake Agent -> Eligibility and Routing Agent
// Payload: ticket_id, requester_email, order_number, product_name, fault_description
// ─────────────────────────────────────────────────────────────────
// AGENT 2: Eligibility and Routing Agent
// ─────────────────────────────────────────────────────────────────
STEP 2.1 — Fetch order from Shopify
shopify.orders.search(email: requester_email)
IF no match AND order_number != null:
shopify.orders.get(order_number)
IF still no match:
zendesk.tickets.update(ticket_id, tags: ['order-not-found'])
sheets.ClaimsTracker.append({ claim_ref: ticket_id, status: 'manual-review' })
EXIT workflow
shopify.order -> {
order_id,
customer_email,
line_items[0].sku, // primary product SKU
line_items[0].price,
created_at, // purchase_date
total_price,
financial_status
}
STEP 2.2 — Lookup warranty rule in Google Sheets
sheets.WarrantyRules.lookup(sku: line_items[0].sku)
-> {
warranty_period_days,
max_auto_resolve_value,
eligible_fault_types[],
default_resolution
}
STEP 2.3 — Eligibility logic
warranty_expiry = created_at + warranty_period_days (days)
eligible = (today <= warranty_expiry)
AND (fault_description matches eligible_fault_types[])
requires_approval = (!eligible) OR (total_price > max_auto_resolve_value)
resolution_type = IF eligible -> default_resolution ELSE 'reject'
STEP 2.4 — Write to Google Sheets tracker
sheets.ClaimsTracker.append({
claim_ref: ticket_id,
customer_email: requester_email,
sku: line_items[0].sku,
order_id: order_id,
purchase_date: created_at,
warranty_expiry: warranty_expiry,
eligible: eligible,
resolution_type: resolution_type,
status: IF requires_approval -> 'pending_approval' ELSE 'auto_approved',
logged_at: now()
})
STEP 2.5 — Branch: requires_approval?
IF requires_approval == true:
slack.chat.postMessage({
channel: '#warranty-approvals',
blocks: block_kit_approval({
ticket_id, requester_email, sku, order_id,
total_price, resolution_type,
approval_reason,
actions: [{ action_id:'approve' }, { action_id:'reject' }],
callback_claim_ref: ticket_id
})
})
// Timeout rule: if no callback within 4 business hours
// -> re-post reminder, tag secondary approver
IF requires_approval == false:
-> pass eligibility_result directly to Agent 3
// HANDOFF: Eligibility and Routing Agent -> Resolution Confirmation Agent
// Payload (auto-resolve): ticket_id, requester_email, resolution_type, requires_approval=false
// Payload (approval path): Slack interactivity callback -> ticket_id, action_id, approver_slack_user_id
// ─────────────────────────────────────────────────────────────────
// AGENT 3: Resolution Confirmation Agent
// ─────────────────────────────────────────────────────────────────
STEP 3.1 — Receive trigger
IF source == 'slack_callback' AND action_id == 'approve':
approved_by = approver_slack_user_id
IF source == 'slack_callback' AND action_id == 'reject':
zendesk.tickets.update(ticket_id, internal_note: rejection_note)
sheets.ClaimsTracker.update(claim_ref: ticket_id, status: 'rejected')
EXIT workflow // support lead handles customer reply manually
IF source == 'auto_resolve':
approved_by = 'auto'
STEP 3.2 — Send resolution confirmation
resolution_message = resolution_template(resolution_type)
zendesk.tickets.update({
ticket_id: ticket_id,
status: 'solved',
public_comment: resolution_message,
tags: ['warranty-resolved', resolution_type]
})
STEP 3.3 — Update tracker
sheets.ClaimsTracker.update({
claim_ref: ticket_id,
status: 'resolved',
resolved_at: now(),
approved_by: approved_by
})
// ─────────────────────────────────────────────────────────────────
// END STATE
// Zendesk ticket: status = 'solved'
// Customer: resolution email received
// Google Sheets tracker: row updated to 'resolved'
// Remaining human steps: finance processes Xero refund; warehouse dispatches replacement
// ─────────────────────────────────────────────────────────────────04Recommended build stack
More documents for this process
Every document generated for Warranty & Guarantee Claim Handling.