Hilo Customer Support DESIGN DOC

Hilo Customer Support - Design Document

Purpose: Problem in, action out - with context as the connective tissue.


Section 1: System Overview

What CS does - problem in, action out

Problem CS Internal Actions External Actions Device User Policy / KB

A problem comes in. CS reasons over device, user, and policy context, and emits a single action - either internal (operational) or external (customer-facing).

Problem · input CS · decision Context · information Action · output

Section 2: Current State - Tools Ecosystem

Today's CS stack. Freshdesk is the ticketing hub (Lucy AI + ticketing). Customer contact arrives via email or web form. The agent pulls context tab-by-tab from five info systems below, and dispatches actions through those same systems plus Jira-CM. No unified context view yet.

Customer contact email · web form EN / FR / DE / IT Freshdesk Lucy AI · Ticketing Internal Actions • Escalate / reassign - Freshdesk routing • Resolve · no-action - close ticket • Agent note - Freshdesk private note External Actions • Customer reply - Freshdesk email • Refund - NetSuite / WooCommerce • Replacement order - NetSuite / WooCommerce • Remote device action - Back Office Tool • Account / subscription - WooCommerce • Engineering ticket - Jira-CM NetSuite orders · tracking shipping · billing WooCommerce orders · pre-orders complete database Back Office Tool SN · email · battery user ID · data gaps Amplitude app version · OS phone model Confluence SOPs · CR · FAQ TS · WOI

Customer contact lands in Freshdesk. The agent pulls context from five info systems (NetSuite, WooCommerce, Back Office Tool, Amplitude, Confluence) - tab by tab, no unified view - then dispatches an internal action (close ticket, escalate, agent note) or an external action (customer reply, refund, replacement, remote device action, account/subscription, Jira engineering ticket).

Customer contact · input Freshdesk · CS hub Info systems · context Action systems · output (incl. Jira-CM)

Section 3: Current State - Request Path

Freshdesk + manual lookups - what happens today, end-to-end, when a customer writes in. Identity threads through email only; context is gathered tab-by-tab. The diagram shows the round-trip for a typical "my cuff isn't syncing" ticket that becomes an RMA + refund.

Rendering…

Why this hurts:

  • 5+ systems, no shared identity. Agent re-keys email/serial across Freshdesk, NetSuite, WooCommerce, Back Office Tool, Amplitude, and Confluence. Nothing auto-links.
  • Context lives in the agent's head. Device telemetry, order history, prior RMAs - all pulled by hand, tab by tab, every time.
  • Customer is the bottleneck. Many tickets stall waiting on the customer to find the serial number on the back of the device.
  • Copy-paste is the integration layer. RMA #, addresses, refund amounts - all manually pasted between NetSuite, WooCommerce, and Freshdesk replies.
  • Multi-day wall-clock for a "simple" case. 5–15 min of agent time per touch, plus return shipping, then a separate touch to refund or ship a replacement.

Section 4: Interim State - Onboarding Intercom + Shopify

The bridge between today and the ideal state. Intercom replaces Freshdesk as the CS hub, and Shopify is now connected - new customer order data flows in automatically. The four legacy tools (NetSuite, WooCommerce, Back Office Tool, Amplitude) still require manual tab-switching until the data lake is live. Confluence stays as the KB fallback.

Customer message Intercom chat EN / FR / DE / IT Intercom Shopify integrated Internal Actions • Escalate / reassign - Intercom routing • Resolve no-action - close conversation • Agent note - Intercom note External Actions • Customer reply - Intercom message • Refund - NetSuite / WooCommerce • Replacement order - NetSuite / WooCommerce • Remote device action - Back Office Tool • Account / subscription - WooCommerce • Engineering ticket - Jira-CM NetSuite orders · tracking legacy WooCommerce orders · subs legacy Shopify new orders customers Back Office Tool SN · battery data gaps Amplitude app · OS user path Freshchat chat history prior sessions Confluence SOPs · FAQ TS · WOI

Intercom is now the CS hub. Shopify customer and order data syncs in automatically (new customers). The four legacy tools below still require manual tab-switching for pre-Shopify customers - this is the gap the data lake closes in the ideal state.

Customer · input Intercom · CS hub (Shopify synced) Legacy info systems · still manual Action systems · output
Rendering...

What improved vs. Section 3:

  • Intercom replaces Freshdesk. Customer messages arrive in Intercom. Replies go back via Intercom chat, not email.
  • Shopify customers have pre-loaded context. For anyone who ordered via Shopify, the agent already has order, warranty, and subscription data in the panel. No NetSuite / WooCommerce lookup needed for them.
  • Legacy customers still require manual tabs. Pre-Shopify customers still need NetSuite + WooCommerce lookups. Back Office Tool and Amplitude are still manual for all customers.
  • RMA and refund actions still manual. No webhook-based fan-out yet - agent still copy-pastes RMA # and processes refunds through NetSuite / WooCommerce directly.

Section 5: Ideal State

Same shape as Section 4. The middle layer has two components: Data Retrieval continuously syncs context from every source into Intercom contact attributes so the agent never opens another tab; Action Handler receives Canvas Kit submits and dispatches them to the right backend API (NetSuite, WooCommerce, Jira-CM, Back Office Tool) in the background.

Customer message Intercom chat EN / FR / DE / IT Intercom Canvas Kit + Copilot + Articles Internal Actions • Escalate / reassign - Intercom routing • Resolve no-action - close conversation • Agent note - Intercom note External Actions • Customer reply - Intercom • Refund - NS + WC • RMA + replacement - NS + WC • Remote device - Back Office Tool • Account / sub - WooCommerce • Engineering ticket - Jira-CM Action Handler → APIs Data Retrieval sources → Intercom NetSuite RMA · orders refund WooCommerce subscription account Shopify customer · orders subscription Back Office Tool device data Amplitude user path · app version Freshchat chat history prior sessions Confluence SOPs · policies KB
Rendering...

What changed vs. Section 4 (interim):

  • Identity is implicit. Contact form attaches to the Shopify customer record at submission. No email threading, no manual lookups.
  • Context arrives pre-loaded. Agent never opens Shopify, NetSuite, or the Hilo admin. The Canvas Kit panel already has device telemetry, order, warranty, and RMA history.
  • Copilot answers policy inline. Agent asks in plain English, Copilot cites the Article. No SOP hunting in Confluence except for true edge cases.
  • One submit = parallel fan-out. The Canvas Kit submit webhook creates the RMA, issues the refund / replacement, and sends the customer reply in one shot.

Section 6: Before / After - RMA + Refund

Same scenario throughout the doc: customer reports their cuff isn't syncing, agent determines RMA + refund is appropriate. Sixteen steps and ~30 minutes today. Eight steps and ~5 minutes after.

Today - Freshdesk
Ticket arrives
Customer fills web form
Lands in Freshdesk queue · may sit for hours
Agent claims ticket from queue
Context gathering
NetSuite tab
Order, serial, RMA history · ~3–5 min
WooCommerce tab
Subscription status · ~2 min
Back Office Tool tab
Firmware, battery, sync gaps · ~2 min
Amplitude tab
App version, last event · ~2 min
Confluence tab
Find replacement & refund SOP · ~1–2 min
Decision & action
Decide: RMA + refund appropriate
NetSuite: fill RMA form
Copy-paste customer info + serial · ~3–5 min
Receive RMA number from NetSuite
Freshdesk: write reply
Paste RMA #, return instructions · ~3 min
Send reply · update status · add notes
Follow-up · days later
Customer sends return tracking number
NetSuite: verify return + QA passed
Process refund in NetSuite
Final reply → close ticket
After - Intercom + Middleware
Ticket arrives
Customer types in Intercom
Conversation opens instantly
Auto
Webhook - Data Retrieval syncs all context
Before agent opens the conversation
Decision & action
Agent opens - Canvas Kit pre-loaded
Device, order, warranty, RMA in sidebar · no tabs
Optional
Ask Copilot: "Eligible for free replacement?"
Instant answer + Article citation
Select "Initiate RMA" · add note · Submit
Auto
Action Handler fires in parallel
NetSuite RMA + refund + customer reply · all three at once
Canvas panel confirms
RMA #, status, next steps
Follow-up · days later
Customer replies in Intercom
Agent checks updated panel · marks closed

Section 7: Integration Spec - Intercom - NetSuite (RMA via Action Handler)

One integration written out end-to-end: trigger, payload, processing chain, NetSuite API call, error handling, and rollback. This is the Action Handler path from Section 5 - the agent clicks Submit in Canvas Kit, and this is what happens in the background.

Trigger

Agent selects case_action: "rma" in the Canvas Kit panel and clicks Submit. Intercom fires a synchronous POST /canvas/submit to the Action Handler. Response must arrive within 10 s or Intercom shows an error to the agent.

Input payload (from Intercom)

{
  "context": {
    "contact_id":      "65abc123",
    "conversation_id": "conv_789xyz",
    "workspace_id":    "qpmg9n5u"
  },
  "input_values": {
    "case_action":  "rma",
    "priority":     "high",
    "agent_note":   "Device stopped syncing after firmware update v2.1.4"
  }
}

Processing chain

  1. Verify HMAC-SHA1 signature on x-hub-signature header. Reject 401 if mismatch.
  2. GET /contacts/{contact_id} from Intercom API - pull email, hilo_serial_number, hilo_warranty_status from contact attributes.
  3. Check warranty eligibility. If ineligible → return Canvas error with reason, stop processing.
  4. POST Return Authorization to NetSuite REST API (payload below).
  5. Receive tranId (RMA number) from NetSuite response.
  6. PUT /contacts/{contact_id} - write hilo_rma_status: "Pending", hilo_rma_number, hilo_rma_netsuite_id, hilo_case_state: "RMA In Progress" back to Intercom.
  7. POST /conversations/{conversation_id}/reply - send customer message: "Your return has been initiated. RMA #: [tranId]. Please ship your device back within 14 days."
  8. Return updated Canvas to Intercom - panel shows RMA number, status, and confirmation text.

NetSuite REST payload

POST https://{accountId}.suitetalk.api.netsuite.com/services/rest/record/v1/returnauthorization
Authorization: Bearer {oauth2_token}

{
  "entity":   { "id": "{netsuite_customer_id}" },
  "memo":     "RMA via Hilo Support - {agent_note}",
  "tranDate": "{today_ISO}",
  "itemList": {
    "item": [{
      "item":        { "id": "{product_item_id}" },
      "quantity":    1,
      "description": "Hilo Band - Serial: {serial_number}",
      "serialNumbers": "{serial_number}"
    }]
  },
  "custbody_hilo_conversation_id": "{conversation_id}",
  "custbody_hilo_serial":          "{serial_number}",
  "custbody_hilo_priority":        "{priority}",
  "custbody_hilo_agent_note":      "{agent_note}"
}

// Response
{ "id": "12345", "tranId": "RMA-2024-00234", "status": "A" }

Error handling

HTTP / Condition Cause Response
401 / 403 Expired OAuth 2.0 token Refresh client-credentials token, retry once. If still failing → Canvas: "Action failed - token error, contact engineering."
404 Customer not found in NetSuite Canvas: "Customer not found in NetSuite - verify email or use manual process." Do not continue.
409 Duplicate RMA for this serial Canvas: "An RMA already exists for this device: [existing RMA #]." Show existing number.
429 NetSuite rate limit Queue with exponential backoff (1 s → 2 s → 4 s). Respond to Canvas immediately with "Processing…". Update Canvas attributes on completion.
500 / timeout >10 s NetSuite down Respond to Intercom Canvas immediately (do not let Intercom time out). Push to dead-letter queue. Alert on-call via Slack. Canvas: "Action queued - RMA will be created within 30 min."

Rollback

  1. At step 6, hilo_rma_netsuite_id is written to the Intercom contact. This is the key for all rollback operations.
  2. Canvas Kit shows a "Cancel RMA" button whenever hilo_rma_status = "Pending".
  3. Cancel flow: DELETE /record/v1/returnauthorization/{netsuite_id} → reset contact attributes (rma_status: "Cancelled", rma_number: "-", case_state: "Open") → log cancellation with timestamp and agent ID.
  4. If the refund was already queued when the cancel fires, issue a compensating DELETE on the NetSuite refund record using the same pattern.