<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nikhil Verma</title>
    <description>The latest articles on DEV Community by Nikhil Verma (@nikhil_verma_dac5a3b39190).</description>
    <link>https://dev.to/nikhil_verma_dac5a3b39190</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3863191%2Ffb6fc10e-f952-4b2a-9fad-54f5be3bfd42.png</url>
      <title>DEV Community: Nikhil Verma</title>
      <link>https://dev.to/nikhil_verma_dac5a3b39190</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikhil_verma_dac5a3b39190"/>
    <language>en</language>
    <item>
      <title>How I Connected Claude to Shopify to Handle Customer Support and Returns</title>
      <dc:creator>Nikhil Verma</dc:creator>
      <pubDate>Mon, 06 Apr 2026 05:52:10 +0000</pubDate>
      <link>https://dev.to/nikhil_verma_dac5a3b39190/how-i-connected-claude-to-shopify-to-handle-customer-support-and-returns-mc5</link>
      <guid>https://dev.to/nikhil_verma_dac5a3b39190/how-i-connected-claude-to-shopify-to-handle-customer-support-and-returns-mc5</guid>
      <description>&lt;p&gt;A client came to me with a problem most small Shopify store owners know well: they were spending 3-4 hours a day just answering the same support emails. Order status. Return requests. Refund questions. Same thing, over and over. &lt;/p&gt;

&lt;p&gt;We built a lightweight Node.js integration that connects Claude directly to the Shopify Admin API. There is no third-party automation tools included and no bloated support platforms. Just Claude reading order data and responding like a smart support agent. &lt;/p&gt;

&lt;p&gt;Here's exactly how we did it. &lt;/p&gt;

&lt;h2&gt;
  
  
  What the system does
&lt;/h2&gt;

&lt;p&gt;When a customer sends a support message, the system: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parses the message to figure out what they want (order status or return/refund) &lt;/li&gt;
&lt;li&gt;Pulls the relevant order data from Shopify &lt;/li&gt;
&lt;li&gt;Feeds that data + the customer's message to Claude &lt;/li&gt;
&lt;li&gt;Sends Claude's response back to the customer &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the request is something Claude can't handle confidently, it flags it for a human. &lt;/p&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js (Express) &lt;/li&gt;
&lt;li&gt;Shopify Admin REST API &lt;/li&gt;
&lt;li&gt;Anthropic Claude API (claude-sonnet-4-20250514) &lt;/li&gt;
&lt;li&gt;A simple webhook endpoint your email/chat tool can hit &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Set up your Shopify API access
&lt;/h2&gt;

&lt;p&gt;Go to your Shopify admin &amp;gt; Settings &amp;gt; Apps and sales channels &amp;gt; Develop apps. Create a private app and give it these scopes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;gt; read_orders 
&amp;gt; read_customers 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save your Admin API access token. You will need it shortly. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Set up your Node.js project
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;mkdir shopify-claude-support &lt;br&gt;
cd shopify-claude-support &lt;br&gt;
npm init -y &lt;br&gt;
npm install express @anthropic-ai/sdk axios dotenv&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Create a .env file: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;SHOPIFY_STORE=your-store.myshopify.com &lt;br&gt;
SHOPIFY_ACCESS_TOKEN=your_shopify_token &lt;br&gt;
ANTHROPIC_API_KEY=your_claude_api_key &lt;br&gt;
PORT=3000&lt;/code&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Pull order data from Shopify
&lt;/h2&gt;

&lt;p&gt;This function takes a customer email and fetches their most recent order, including line items and fulfillment status. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;`// shopify.js &lt;br&gt;
const axios = require('axios'); &lt;br&gt;
require('dotenv').config(); &lt;/p&gt;

&lt;p&gt;async function getOrderByEmail(email) { &lt;br&gt;
 const url = &lt;code&gt;https://${process.env.SHOPIFY_STORE}/admin/api/2024-01/orders.json&lt;/code&gt;; &lt;/p&gt;

&lt;p&gt;const response = await axios.get(url, { &lt;br&gt;
   headers: { &lt;br&gt;
     'X-Shopify-Access-Token': process.env.SHOPIFY_ACCESS_TOKEN, &lt;br&gt;
     'Content-Type': 'application/json' &lt;br&gt;
   }, &lt;br&gt;
   params: { &lt;br&gt;
     email: email, &lt;br&gt;
     status: 'any', &lt;br&gt;
     limit: 5 &lt;br&gt;
   } &lt;br&gt;
 }); &lt;/p&gt;

&lt;p&gt;const orders = response.data.orders; &lt;br&gt;
 if (!orders || orders.length === 0) return null; &lt;/p&gt;

&lt;p&gt;// Return the most recent order &lt;br&gt;
 return orders[0]; &lt;br&gt;
} &lt;/p&gt;

&lt;p&gt;module.exports = { getOrderByEmail }; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;`&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Build the Claude response function
&lt;/h2&gt;

&lt;p&gt;This is where the actual intelligence lives. We pass Claude the order details and the customer's message, and tell it exactly what it can and can't do. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;// claude.js &lt;br&gt;
const Anthropic = require('@anthropic-ai/sdk'); &lt;br&gt;
require('dotenv').config(); &lt;/p&gt;

&lt;p&gt;const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }); &lt;/p&gt;

&lt;p&gt;async function generateSupportResponse(customerMessage, orderData) { &lt;br&gt;
 const orderContext = orderData &lt;br&gt;
   ? &lt;code&gt;&lt;br&gt;
Order ID: ${orderData.name} &lt;br&gt;
Status: ${orderData.financial_status} / ${orderData.fulfillment_status || 'unfulfilled'} &lt;br&gt;
Items: ${orderData.line_items.map(i =&amp;gt;&lt;/code&gt;${i.name} (qty: ${i.quantity})&lt;code&gt;).join(', ')} &lt;br&gt;
Total: ${orderData.currency} ${orderData.total_price} &lt;br&gt;
Created: ${new Date(orderData.created_at).toDateString()} &lt;br&gt;
Tracking: ${orderData.fulfillments?.[0]?.tracking_number || 'Not available yet'} &lt;br&gt;
&lt;/code&gt; &lt;br&gt;
   : 'No order found for this customer email.'; &lt;/p&gt;

&lt;p&gt;const systemPrompt = `You are a helpful customer support agent for an online store. &lt;br&gt;
You have access to the customer's order information. Use it to answer their question clearly and accurately. &lt;/p&gt;

&lt;p&gt;Rules: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For order status questions: give a direct answer based on the data provided. &lt;/li&gt;
&lt;li&gt;For return or refund requests: tell them the return window is 30 days from delivery, and instruct them to reply with their reason. Do not process returns yourself. &lt;/li&gt;
&lt;li&gt;If you don't have enough information to answer confidently, say so and tell the customer a human agent will follow up within 24 hours. &lt;/li&gt;
&lt;li&gt;Keep responses short and friendly. No corporate fluff. &lt;/li&gt;
&lt;li&gt;Never make up order details that aren't in the data.`; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;const userMessage = &lt;code&gt;Customer message: ${customerMessage}\n\nOrder data:\n${orderContext}&lt;/code&gt;; &lt;/p&gt;

&lt;p&gt;const response = await client.messages.create({ &lt;br&gt;
   model: 'claude-sonnet-4-20250514', &lt;br&gt;
   max_tokens: 500, &lt;br&gt;
   system: systemPrompt, &lt;br&gt;
   messages: [{ role: 'user', content: userMessage }] &lt;br&gt;
 }); &lt;/p&gt;

&lt;p&gt;return response.content[0].text; &lt;br&gt;
} &lt;/p&gt;

&lt;p&gt;module.exports = { generateSupportResponse }; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A few things worth noting in the system prompt:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;We explicitly tell Claude what it can and cannot do. It collects the intent and hands off. &lt;/li&gt;
&lt;li&gt;The "never make up order details" line matters. Without it, Claude will sometimes fill gaps confidently with hallucinated data. &lt;/li&gt;
&lt;li&gt;The 24-hour human escalation clause keeps customers from being left hanging. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5: Wire it together with a webhook endpoint
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;// index.js &lt;br&gt;
const express = require('express'); &lt;br&gt;
const { getOrderByEmail } = require('./shopify'); &lt;br&gt;
const { generateSupportResponse } = require('./claude'); &lt;br&gt;
require('dotenv').config(); &lt;/p&gt;

&lt;p&gt;const app = express(); &lt;br&gt;
app.use(express.json()); &lt;/p&gt;

&lt;p&gt;app.post('/support', async (req, res) =&amp;gt; { &lt;br&gt;
 const { customer_email, message } = req.body; &lt;/p&gt;

&lt;p&gt;if (!customer_email || !message) { &lt;br&gt;
   return res.status(400).json({ error: 'customer_email and message are required' }); &lt;br&gt;
 } &lt;/p&gt;

&lt;p&gt;try { &lt;br&gt;
   const orderData = await getOrderByEmail(customer_email); &lt;br&gt;
   const reply = await generateSupportResponse(message, orderData); &lt;/p&gt;

&lt;p&gt;res.json({ &lt;br&gt;
     reply, &lt;br&gt;
     order_found: !!orderData, &lt;br&gt;
     order_id: orderData?.name || null &lt;br&gt;
   }); &lt;br&gt;
 } catch (err) { &lt;br&gt;
   console.error(err); &lt;br&gt;
   res.status(500).json({ error: 'Something went wrong. Please try again.' }); &lt;br&gt;
 } &lt;br&gt;
}); &lt;/p&gt;

&lt;p&gt;app.listen(process.env.PORT, () =&amp;gt; { &lt;br&gt;
 console.log(&lt;code&gt;Support server running on port ${process.env.PORT}&lt;/code&gt;); &lt;br&gt;
}); &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Testing it
&lt;/h2&gt;

&lt;p&gt;Start the server: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;node index.js &lt;br&gt;
&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Send a test request: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;curl -X POST &lt;a href="http://localhost:3000/support" rel="noopener noreferrer"&gt;http://localhost:3000/support&lt;/a&gt; \ &lt;br&gt;
 -H "Content-Type: application/json" \ &lt;br&gt;
 -d '{ &lt;br&gt;
   "customer_email": "&lt;a href="mailto:customer@example.com"&gt;customer@example.com&lt;/a&gt;", &lt;br&gt;
   "message": "Hey, where is my order? I ordered 5 days ago." &lt;br&gt;
 }' &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the customer has an order in Shopify, Claude will pull the data and respond with the actual status and tracking info. If not, it will tell them a human will follow up. &lt;/p&gt;

&lt;h2&gt;
  
  
  What Claude handles well vs. what it doesn't
&lt;/h2&gt;

&lt;p&gt;*&lt;em&gt;Handles well: &lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order status questions ("Where is my package?") &lt;/li&gt;
&lt;li&gt;Basic return eligibility ("Can I return this?") &lt;/li&gt;
&lt;li&gt;Reformatting messy questions into clean answers &lt;/li&gt;
&lt;li&gt;Staying calm with frustrated customers &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;*&lt;em&gt;Where you still need a human: &lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex refund disputes &lt;/li&gt;
&lt;li&gt;Lost packages requiring carrier investigation &lt;/li&gt;
&lt;li&gt;Cases where the customer's email doesn't match the order &lt;/li&gt;
&lt;li&gt;Anything involving payment changes &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For those cases, the system flags the conversation and drops it into a queue for manual review. We keep a simple escalation_needed boolean in the response for this purpose. &lt;/p&gt;

&lt;h2&gt;
  
  
  What this saved the client
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before: **~3.5 hours/day on support emails. &lt;br&gt;
**After:&lt;/strong&gt; ~40 minutes, mostly on escalated cases. &lt;/p&gt;

&lt;p&gt;Claude handles roughly 70-75% of incoming tickets fully on its own. The rest either need a human or the customer email didn't match an order in the system. &lt;/p&gt;

&lt;p&gt;Total build time was about two days including testing. Cost of running it is almost nothing; a few dollars a month in API calls at the volume this client gets. &lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;A few things we're adding to this &lt;a href="https://aiintegrator.in/" rel="noopener noreferrer"&gt;AI integration&lt;/a&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order lookup by order number (not just email) since customers often don't use the email they ordered with &lt;/li&gt;
&lt;li&gt;A basic classification step before hitting Claude, so obvious spam doesn't burn tokens &lt;/li&gt;
&lt;li&gt;Logging escalated tickets to a simple Notion database for the client to review &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build something similar or have questions about the setup, drop a comment. Happy to help troubleshoot. &lt;/p&gt;

</description>
      <category>ecommerce</category>
      <category>claude</category>
      <category>ai</category>
      <category>shopify</category>
    </item>
  </channel>
</rss>
