
Customer Support Integration
- 66 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Connect a helpdesk like Gorgias, Zendesk, or Intercom to your store so agents see order history and customer spend inside each ticket.
About
Surfaces order context in support tickets via native apps or a custom sidebar, and routes high-value tickets to VIP queues. A developer uses it when agents keep asking customers for order numbers or need value-based ticket routing.
- Per-platform helpdesk recommendations and Gorgias automation rules
- Custom Zendesk context endpoint plus VIP routing and auto-ticket-on-delivery-failure code
Customer Support Integration by the numbers
- 66 all-time installs (skills.sh)
- Ranked #971 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill customer-support-integrationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 66 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Connect a helpdesk like Gorgias, Zendesk, or Intercom to your store so agents see order history and customer spend inside each ticket.
Files
Customer Support Integration
Overview
Connecting your helpdesk to your store automatically surfaces order history, tracking information, and customer spend inside every support ticket — reducing average handle time by 40–60% because agents don't switch between systems. For Shopify, Gorgias is the purpose-built helpdesk that's native to the platform. For other platforms and for Zendesk/Intercom users, dedicated integration apps connect the systems. Only build a custom integration if you need deep two-way automation (auto-create tickets from order events, VIP routing, CSAT sync back to CRM) that off-the-shelf apps don't provide.
When to Use This Skill
- When support agents repeatedly ask customers for their order number because it doesn't auto-populate in the ticket
- When implementing Zendesk Sunshine Apps or Intercom Canvas Kit to show order details inside the agent interface
- When automating ticket creation from order events (failed delivery, fraud hold, backorder)
- When routing tickets by order value to prioritize VIP customers
- When syncing support CSAT scores back to your CRM for customer health scoring
Core Instructions
Step 1: Determine platform and choose the right helpdesk
| Platform | Best Helpdesk | Why |
|---|---|---|
| Shopify | Gorgias | Purpose-built for Shopify; deep order data access, macro variables that pull order info, 1-click actions (refund, cancel, reorder) from within the ticket |
| Shopify | Zendesk | Good for teams that already use Zendesk; install the Shopify app for Zendesk from the App Store |
| WooCommerce | Gorgias or Freshdesk | Gorgias supports WooCommerce; Freshdesk + WooCommerce plugin for teams on Freshdesk |
| BigCommerce | Gorgias or Zendesk | Both have BigCommerce integrations in their app marketplaces |
| Custom / Headless | Zendesk or Intercom with custom integration | Build a sidebar app to inject order context into tickets |
---
Step 2: Platform-specific setup
---
Shopify
Option A: Gorgias (recommended for Shopify)
Gorgias is the most widely-used Shopify support helpdesk with the deepest platform integration.
1. Install Gorgias from the Shopify App Store 2. Authorize Gorgias to access your Shopify store data 3. Gorgias automatically pulls order history, customer details, and shipping status into every ticket when a customer emails from the address on their Shopify account
What Gorgias shows agents automatically:
- Customer name, email, total spent, order count
- All past orders with status, items, and tracking
- Live chat history across all channels
Setting up automation rules: 1. Go to Gorgias → Automation → Rules 2. Create rules like:
- Auto-tag tickets with "VIP" when customer lifetime value > $500
- Auto-assign VIP tickets to a senior support team
- Auto-reply with order status when ticket contains "where is my order"
1-click actions from within Gorgias:
- Refund, cancel, or duplicate an order directly from the ticket sidebar
- Apply discount codes to orders without leaving Gorgias
- Create a draft order for a replacement
Gorgias macros (template responses with dynamic variables):
- Create macros that pull in order data automatically:
Your order {{order.name}} is currently {{order.fulfillment_status}} - Go to Settings → Macros to create and manage macros
---
WooCommerce
Gorgias for WooCommerce:
1. Install the Gorgias WooCommerce plugin from WordPress.org or connect via Gorgias integrations 2. Connect your WooCommerce store — Gorgias pulls order history automatically
Freshdesk for WooCommerce: 1. Install Freshdesk Help Desk for WooCommerce from the WordPress plugin directory 2. The plugin creates Freshdesk tickets from WooCommerce order events 3. Freshdesk agents see customer order details in the ticket sidebar via the integration
Manual integration with Zendesk: 1. Install Zendesk for WooCommerce from the Zendesk App Marketplace 2. Agents can search for customers by email and see their order history in the ticket sidebar
---
BigCommerce
Gorgias for BigCommerce: 1. Install Gorgias from the BigCommerce App Marketplace 2. Connect your store — same deep integration as Shopify
Zendesk for BigCommerce: 1. Install the BigCommerce app from the Zendesk App Marketplace 2. Agents see customer details and order history in the ticket sidebar
---
Custom / Headless
Build a Zendesk Sunshine App (sidebar panel) or Intercom Canvas Kit app that injects order context into every ticket:
Zendesk sidebar app — data endpoint:
// GET /api/support/zendesk-context?email=customer@example.com
export async function getZendeskContext(req: Request, res: Response) {
const customerEmail = (req.query.email as string)?.toLowerCase();
if (!customerEmail) return res.json({ customer: null, orders: [] });
const [customer, recentOrders] = await Promise.all([
db.customers.findByEmail(customerEmail, { include: ['segmentScore'] }),
db.orders.findMany({
where: { customerEmail },
orderBy: { createdAt: 'desc' },
take: 5,
include: { lineItems: { include: { product: true } }, shipments: true },
}),
]);
res.json({
customer: customer ? {
lifetimeValue: customer.totalSpentCents / 100,
orderCount: customer.orderCount,
segment: customer.segmentScore?.segment,
tags: customer.tags,
} : null,
orders: recentOrders.map(o => ({
number: o.orderNumber,
status: o.status,
total: o.totalCents / 100,
createdAt: o.createdAt,
trackingUrl: o.shipments[0]?.trackingUrl,
items: o.lineItems.map(i => ({ name: i.product.name, quantity: i.quantity })),
})),
});
}Route high-value tickets to VIP queue:
// Called when a new Zendesk ticket is created (via Zendesk webhook)
export async function applyTicketRouting(ticketId: string) {
const ticket = await fetchZendeskTicket(ticketId);
const customerEmail = ticket.requester?.email?.toLowerCase();
if (!customerEmail) return;
const customer = await db.customers.findByEmail(customerEmail, { include: ['segmentScore'] });
if (!customer) return;
const isVIP = ['champions', 'cannot_lose_them'].includes(customer.segmentScore?.segment ?? '');
const isHighValue = customer.totalSpentCents >= 100000; // $1,000+
if (isVIP || isHighValue) {
await fetch(`https://${process.env.ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/tickets/${ticketId}.json`, {
method: 'PUT',
headers: { Authorization: getZendeskAuthHeader(), 'Content-Type': 'application/json' },
body: JSON.stringify({
ticket: {
priority: 'urgent',
group_id: process.env.ZENDESK_VIP_GROUP_ID,
tags: [...(ticket.tags ?? []), 'vip-customer'],
},
}),
});
}
}
// Auto-create ticket on delivery failure
export async function onDeliveryFailed(shipmentId: string) {
const shipment = await db.shipments.findById(shipmentId, { include: ['order.customer'] });
await fetch(`https://${process.env.ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/tickets.json`, {
method: 'POST',
headers: { Authorization: getZendeskAuthHeader(), 'Content-Type': 'application/json' },
body: JSON.stringify({
ticket: {
subject: `Delivery failed — Order #${shipment.order.orderNumber}`,
comment: { body: `Delivery attempt failed on ${new Date().toDateString()}. Carrier: ${shipment.carrier}. Tracking: ${shipment.trackingNumber}.` },
requester: { email: shipment.order.customer.email },
priority: 'high',
tags: ['delivery-failure', 'auto-created'],
},
}),
});
}---
Step 3: Set up ticket routing rules for VIP customers
Ensure your most valuable customers get faster responses by routing their tickets to your best agents.
Gorgias routing rules: 1. Go to Automation → Rules → Create Rule 2. Condition: Customer → Total spent → is greater than → $500 3. Actions: Add tag → vip, Assign to → VIP Support Team, Set priority → Urgent
Zendesk trigger: 1. Go to Admin → Business Rules → Triggers → Add trigger 2. Conditions: ticket tag contains "vip-customer" 3. Actions: assign to group (VIP Support), set priority to Urgent
Define SLA targets:
- VIP customers: first response within 1 hour
- Standard customers: first response within 24 hours
- Set these in Gorgias → Settings → Business hours and SLAs or Zendesk → Admin → SLAs
Best Practices
- Use Gorgias for Shopify stores — it's the purpose-built solution; the time saved on setup and the depth of integration are worth the subscription cost
- Never store helpdesk API tokens in client-side code — Zendesk and Intercom API tokens have write access to all tickets; always proxy requests through your server
- Attach the order ID to every ticket — this is the key that links your support system to your commerce database and enables two-way sync and automation
- Keep order context fresh in the sidebar — cache data for 60 seconds maximum; an agent seeing stale order status is worse than a loading spinner
- Log every agent action taken on an order — maintain an audit trail of refunds, order changes, and status updates initiated from within the helpdesk
Common Pitfalls
| Problem | Solution |
|---|---|
| Agent sees wrong customer because email lookup is case-sensitive | Normalize all emails to lowercase before lookup; Jane@example.com and jane@example.com must resolve to the same customer |
| Webhook payload not verified | Implement HMAC signature verification using the Zendesk/Gorgias webhook signing secret before processing any payload |
| CSAT sync creates duplicate customer records | Always look up by email first; never create a new customer record from a support webhook — link to existing or skip |
| Auto-created tickets missing order context | Set the order ID in a custom ticket field at creation time; this enables bidirectional sync and accurate routing |
Related Skills
- @live-chat-commerce
- @customer-segmentation
- @customer-lifetime-value
{
"context": "Tests whether the agent correctly implements an Intercom Canvas Kit endpoint that renders order context in the conversation sidebar, using the correct component schema, conditionally including a tracking button, handling missing order data gracefully, and meeting Intercom's performance constraints.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Canvas content structure",
"max_score": 10,
"description": "Response has a top-level 'content' key containing a 'components' array (i.e., { content: { components: [...] } })"
},
{
"name": "Header component for order",
"max_score": 8,
"description": "Uses a component with style 'header' (or equivalent heading) to display the order number or 'Last Order' label"
},
{
"name": "Paragraph/muted text components",
"max_score": 8,
"description": "Uses components with style 'paragraph' or 'muted' for order status, total, and placed date fields"
},
{
"name": "Track Package button type",
"max_score": 10,
"description": "The tracking button uses type 'button' (not 'link' or 'anchor')"
},
{
"name": "Track Package button action",
"max_score": 10,
"description": "The tracking button's action uses type 'url' pointing to the shipment tracking URL"
},
{
"name": "Conditional tracking button",
"max_score": 10,
"description": "The 'Track Package' button is only included when a tracking URL is present — not rendered when tracking URL is null/undefined"
},
{
"name": "No-order fallback",
"max_score": 8,
"description": "Returns a canvas with a paragraph-style text component saying no orders found (or equivalent) when the customer has no recent orders"
},
{
"name": "Email normalization",
"max_score": 8,
"description": "Normalizes the customer email to lowercase before the order database lookup"
},
{
"name": "Performance awareness",
"max_score": 8,
"description": "Implementation notes or code comments acknowledge the 5-second Intercom timeout constraint, or the query uses indexing/limiting strategies to stay under 2 seconds"
},
{
"name": "Contact email extraction",
"max_score": 8,
"description": "Extracts the customer email from req.body.contact.email (or equivalent canvas kit payload structure)"
},
{
"name": "Total formatted as currency",
"max_score": 6,
"description": "Displays order total formatted as a dollar amount (converts cents to dollars, e.g., totalCents / 100 with 2 decimal places)"
},
{
"name": "Server-side only",
"max_score": 6,
"description": "Implementation is a server-side HTTP handler with no client-side credential exposure"
}
]
}
Order Context Panel for Intercom Support Conversations
Problem/Feature Description
GreenLeaf Commerce's support team uses Intercom as their primary customer messaging platform. Right now, when a customer messages about a missing package or a wrong item, the support agent has to open a separate browser tab, log into the order management system, search for the customer, and manually read off order details. This context-switching adds 2–3 minutes per conversation and leads to mistakes during busy periods.
The engineering team wants to build a custom sidebar panel for Intercom that automatically displays the customer's most recent order — including status, total, placement date, and a tracking link — the moment a conversation is opened. The panel should be implemented using Intercom's native mechanism for embedding custom content in the agent workspace, and it must respond fast enough to keep up with a busy support queue.
Output Specification
Produce a TypeScript source file named intercom-canvas-handler.ts containing:
- The HTTP POST handler that Intercom calls when a conversation is opened
- All canvas rendering logic (components for order data and fallback when no orders exist)
- Environment variable references for any credentials or IDs
Also produce implementation-notes.md explaining:
- The response structure your handler returns
- How performance requirements influenced the implementation
- How the handler handles cases where the customer has no orders or no tracking information
Input Files
The following file describes the order data structure available from the database layer. Extract it before beginning.
=============== FILE: inputs/order-model.ts =============== export interface Order { id: string; number: string; // human-readable e.g. "ORD-1042" status: string; // e.g. "shipped", "delivered", "processing" totalCents: number; // integer, in cents createdAt: Date; shipments: Array<{ trackingUrl: string | null; carrier: string; }>; }
// Available DB function: // db.orders.findLatestByEmail(email: string, options?: { include?: string[] }): Promise<Order | null>
{
"context": "Tests whether the agent correctly implements automated Zendesk ticket creation from order events, using proper authentication, attaching the order ID as a custom field, applying the correct tags, and normalizing customer emails before lookup.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Basic auth format",
"max_score": 10,
"description": "Uses Basic authentication with Buffer.from(`${email}/token:${token}`).toString('base64') pattern (i.e., email/token: prefix in the base64-encoded credentials)"
},
{
"name": "Order ID custom field",
"max_score": 12,
"description": "Attaches the order ID to the ticket via a custom_fields array entry using a configurable field ID (environment variable), not as a tag or body text alone"
},
{
"name": "auto-created tag",
"max_score": 8,
"description": "Includes 'auto-created' tag on every programmatically created ticket"
},
{
"name": "Event-specific tag",
"max_score": 8,
"description": "Includes an event-specific tag (e.g., 'delivery-failure') on the ticket, distinct from 'auto-created'"
},
{
"name": "order-ID tag",
"max_score": 8,
"description": "Includes a tag in the format 'order-{orderId}' on the ticket"
},
{
"name": "Email normalization",
"max_score": 10,
"description": "Normalizes the customer email to lowercase before any database lookup (e.g., .toLowerCase() or equivalent)"
},
{
"name": "Zendesk API endpoint",
"max_score": 8,
"description": "Uses the correct Zendesk REST API URL pattern: https://{subdomain}.zendesk.com/api/v2/tickets.json with POST method"
},
{
"name": "Ticket priority field",
"max_score": 7,
"description": "Sets a priority field on the ticket (one of: urgent, high, normal, low)"
},
{
"name": "Requester by email",
"max_score": 7,
"description": "Sets the ticket requester using the customer's email address (requester: { email: ... })"
},
{
"name": "Server-side token",
"max_score": 8,
"description": "API token is used only server-side (no client-side exposure); the implementation is an HTTP handler/endpoint, not client-side JavaScript"
},
{
"name": "Includes order detail in body",
"max_score": 7,
"description": "The ticket body/comment includes relevant order information (order number, carrier, tracking number, or failure date)"
},
{
"name": "No new customer on webhook",
"max_score": 7,
"description": "Does NOT create a new customer record when processing the event — looks up existing customer by email only"
}
]
}
Automated Helpdesk Tickets for Order Delivery Issues
Problem/Feature Description
ShipRight, a mid-sized e-commerce company, is dealing with an operational headache: when a carrier marks a package as undeliverable, customers have no idea what's happening and support agents only find out when an angry email arrives two days later. The ops team wants to close this gap by automatically opening a support ticket in Zendesk the moment their order management system registers a delivery failure, so the support team can get ahead of the issue before the customer even notices.
The company already has a Zendesk account and an internal order database. They need a TypeScript HTTP endpoint (Express-style) that their order management system can call whenever a shipment's delivery attempt fails. The endpoint must create a properly formatted Zendesk ticket, tag and categorize it correctly so that automated vs. manually-created tickets can be distinguished in reports, and ensure the order is linked to the ticket in a way that enables future two-way synchronization between the order database and Zendesk.
Output Specification
Produce a TypeScript source file named create-ticket-handler.ts containing:
- The HTTP handler function for the delivery failure event
- Any helper functions needed for authentication or ticket creation
- Environment variable references (not hardcoded values) for credentials and configuration
- A brief
implementation-notes.mdexplaining: how authentication is constructed, how the order is linked to the ticket, and what tags are applied and why
The handler should accept a JSON body describing the failed shipment and create the corresponding Zendesk ticket.
Input Files
The following file describes the data shape available from the order management system when a delivery failure occurs. Extract it before beginning.
=============== FILE: inputs/shipment-failure-event.ts =============== export interface ShipmentFailureEvent { shipmentId: string; orderId: string; orderNumber: string; // human-readable e.g. "ORD-4821" carrier: string; // e.g. "FedEx" trackingNumber: string; customerEmail: string; // may be mixed case from the OMS failedAt: string; // ISO 8601 timestamp }
{
"context": "Tests whether the agent correctly handles Zendesk webhooks with HMAC signature verification, syncs CSAT scores back to the CRM with proper flagging for poor experiences, and applies VIP routing rules based on customer segments and lifetime spend thresholds.",
"type": "weighted_checklist",
"checklist": [
{
"name": "HMAC verification present",
"max_score": 12,
"description": "The webhook handler includes HMAC signature verification before processing the payload (checks a signature header against a computed HMAC using the signing secret)"
},
{
"name": "HMAC rejects invalid",
"max_score": 8,
"description": "Returns a non-200 status code (e.g., 401 or 403) when the HMAC signature does not match, before processing the payload"
},
{
"name": "CSAT score field",
"max_score": 8,
"description": "Updates the customer record with lastCsatScore set to the satisfaction score value from the webhook payload"
},
{
"name": "CSAT timestamp field",
"max_score": 7,
"description": "Updates the customer record with lastCsatAt set to the current date/time (new Date() or equivalent)"
},
{
"name": "poor_support_experience flag",
"max_score": 8,
"description": "Creates a customerFlag record with flag value 'poor_support_experience' when satisfaction score is 'bad'"
},
{
"name": "No flag for good CSAT",
"max_score": 5,
"description": "Does NOT create a poor_support_experience flag entry when satisfaction score is 'good'"
},
{
"name": "VIP segment routing",
"max_score": 10,
"description": "Assigns 'urgent' priority to tickets where customer segment is 'champions' or 'cannot_lose_them'"
},
{
"name": "VIP group assignment",
"max_score": 8,
"description": "Assigns the VIP group ID (from environment variable) to tickets for 'champions' or 'cannot_lose_them' customers"
},
{
"name": "High-value spend threshold",
"max_score": 8,
"description": "Assigns 'high' priority (not 'urgent') for customers with lifetime spend at or above $1000 (100000 cents) who are NOT in a VIP segment"
},
{
"name": "No duplicate customer creation",
"max_score": 8,
"description": "Looks up customers by email only — does NOT call any create/insert function for a customer record when processing webhook events"
},
{
"name": "Event type filtering",
"max_score": 8,
"description": "Only processes CSAT logic when the webhook event type is 'ticket.satisfaction_rating.created' (ignores other event types)"
},
{
"name": "Returns 200 promptly",
"max_score": 10,
"description": "Sends a 200 response after processing (e.g., res.sendStatus(200) or res.status(200).send()), not withholding until all async work completes in a way that would cause timeout"
}
]
}
Customer Satisfaction Sync and Smart Ticket Routing
Problem/Feature Description
StyleHub, an online fashion retailer, has been using Zendesk for support but their CRM has no visibility into how support interactions go. Marketing has no way to flag recently disappointed customers before sending a promotional campaign, and support managers can't prioritize their best customers when the queue gets long. Two features are urgently needed: first, when a customer rates a resolved ticket, that satisfaction score should flow back into the CRM immediately so marketing can react; second, when a new ticket comes in, it should automatically be escalated and routed to the right agent group based on how valuable that customer is.
The engineering team needs a TypeScript module implementing two things: a Zendesk webhook handler that securely receives ticket events and syncs satisfaction ratings into the customer database, and a ticket routing function that adjusts priority and agent group assignment based on customer segments. The webhook must be hardened against spoofed payloads. Customer lookup must never accidentally introduce duplicate records.
Output Specification
Produce a TypeScript source file named zendesk-webhook-handler.ts containing:
- The HTTP POST webhook handler that processes incoming Zendesk ticket events
- The ticket routing function that sets priority and group based on customer segment/value
- Helper functions for authentication/verification as needed
- Environment variable references for secrets, subdomain, group IDs, and field IDs
Also produce implementation-notes.md explaining:
- How the webhook payload is verified before processing
- What happens when a 'bad' satisfaction rating is received vs. a 'good' one
- How the routing rules work and what thresholds are used
Input Files
The following files describe the database interfaces and customer model available. Extract them before beginning.
=============== FILE: inputs/db-types.ts =============== export interface Customer { id: string; email: string; // stored normalized lifetimeSpendCents: number; // integer segmentScore?: { segment: string; // e.g. 'champions', 'cannot_lose_them', 'at_risk', 'new_customers' }; }
// Available DB functions: // db.customers.findByEmail(email: string, options?: { include?: string[] }): Promise<Customer | null> // db.customers.updateByEmail(email: string, data: Partial<Customer & { lastCsatScore: string; lastCsatAt: Date }>): Promise<void> // db.customerFlags.create(data: { email: string; flag: string; createdAt: Date }): Promise<void>
=============== FILE: inputs/zendesk-api.ts =============== // Helper already available in your codebase: // fetchZendeskTicket(ticketId: string): Promise<ZendeskTicket>
export interface ZendeskTicket { id: number; requester?: { email?: string }; custom_fields?: Array<{ id: string; value: string }>; satisfaction?: { score: 'good' | 'bad' }; }
{
"name": "finsi/customer-support-integration",
"version": "0.1.0",
"summary": "Helpdesk integration (Zendesk, Intercom) with order context injection",
"skills": {
"customer-support-integration": {
"path": "SKILL.md"
}
}
}