{
  "id": "DJ6SDGB0vM8Mtick",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Shopping Assistant - Agent + Function Calling (Product APIs)",
  "tags": [],
  "nodes": [
    {
      "id": "1c0379f2-93c5-4530-9c2e-7176b38b0323",
      "name": "Sticky Note - Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6656,
        1952
      ],
      "parameters": {
        "width": 1020,
        "height": 988,
        "content": "## Shopping Assistant Agent (Function Calling over Product APIs)\n\nA conversational shopping assistant that can search a product catalog, recommend items, and \u2014 with an explicit human-in-the-loop approval step \u2014 place a real order through a store/checkout API. Built as an AI Agent with a tool (function call) for product search, plus wait steps for human confirmation and for the async order-confirmation callback.\n\n### How it works\n1. User sends a shopping request in chat (or the flow is triggered manually for testing)\n2. Set - Config centralizes API base URLs, currency, customer/shipping defaults and the Slack channel\n3. AI Agent - Shopping Assistant (GPT-4.1-mini + memory) chats with the user and, via function calling, invokes the 'Search Products API' tool whenever it needs live catalog data (price, stock, availability)\n4. The agent's final structured reply is parsed: a reply message, an optional shortlist of products, and a purchaseIntent flag with the selected product/quantity\n5. If the user wants to buy something, the flow pauses (Wait) for the user to confirm which item, pauses again (Wait) for explicit purchase approval, then calls the Create Order (Checkout) API\n6. Because order/payment processing is often asynchronous, the flow pauses a third time (Wait) for the store's order-confirmation webhook\n7. A final message (order id, total, ETA) is posted to Slack / back to the customer\n\n### How to set up\n1. Import this workflow into n8n\n2. Add credentials: OpenAI (Chat Model), a product-catalog API auth (header/API key on the 'Search Products API' tool), a store/checkout API auth (on 'Create Order'), and Slack (chat:write)\n3. Edit Set - Config: apiBaseUrl (catalog search), orderApiBaseUrl (checkout), currency, customerId, defaultShippingAddress, maxBudget, slackChannel\n4. Point the two Wait nodes' resume webhooks at your chat UI's 'confirm selection' / 'approve purchase' buttons (or a Slack interactive message)\n5. Point the third Wait node's resume webhook at your store/payment provider's order-confirmation callback\n6. Activate the workflow, or use the Manual Trigger to test end to end with a sample message\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "52a393fb-7ca5-4a88-b8cb-5e42d7f7d7a3",
      "name": "Sticky Note - Agent & Search",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7856,
        1888
      ],
      "parameters": {
        "color": 4,
        "width": 884,
        "height": 1044,
        "content": "## 1. Trigger & Agent (Function Calling)\n\nSet - Config centralizes the catalog/checkout API URLs, currency, customer/shipping defaults and Slack channel.\n\nAI Agent - Shopping Assistant is a LangChain agent backed by the OpenAI Chat Model and a short-term Window Buffer Memory (so the conversation stays coherent across turns). It has ONE function-calling tool wired in: HTTP Request Tool - Search Products API. The agent decides for itself when it needs live catalog data and calls the tool with parameters (query, maxPrice, category) extracted from the conversation.\n\nThe agent is prompted to always finish with a small structured JSON payload (reply text, a product shortlist, and a purchaseIntent flag) so the rest of the flow can act on it deterministically."
      },
      "typeVersion": 1
    },
    {
      "id": "d5158e7f-b616-4575-bd5a-b02b30fa2169",
      "name": "Sticky Note - Selection & Approval",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        8832,
        1872
      ],
      "parameters": {
        "color": 3,
        "width": 1364,
        "height": 1124,
        "content": "## 2. Parse Output, Selection & Purchase Approval\n\nJS - Parse Agent Output & Build Shortlist safely parses the agent's JSON, falling back to a plain-text reply if parsing fails.\n\nIF - Purchase Intent Detected routes: no purchase intent -> Set - Direct Answer Response (just reply to the user); purchase intent -> Wait - For User Product Selection, which pauses the workflow until the customer confirms which shortlisted item they want (resumed via webhook from the chat UI).\n\nIF - Selection Confirmed then gates a second pause, Wait - For Purchase Approval \u2014 a deliberate human-in-the-loop safety check before any money moves, resumed via webhook once the customer (or an approver) explicitly says yes."
      },
      "typeVersion": 1
    },
    {
      "id": "0644fbdf-dfb9-4b70-a040-6ab7d107f080",
      "name": "Sticky Note - Checkout & Confirmation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        10224,
        1824
      ],
      "parameters": {
        "color": 6,
        "width": 1648,
        "height": 900,
        "content": "## 3. Checkout, Async Confirmation & Notify\n\nIF - Purchase Approved gates the actual order: HTTP Request - Create Order (Checkout API) submits the selected product, quantity, customer id and shipping address to the store's order-creation endpoint.\n\nBecause many checkout/payment APIs confirm asynchronously, Wait - For Order Confirmation Webhook pauses until the store posts back an order-status callback (paid / shipped / failed).\n\nJS - Parse Order Confirmation & Build Notification turns that callback into a clean message (order id, status, total, ETA), and Slack - Notify Customer posts it to the configured channel. Every HTTP/API call uses continueOnFail so a downstream hiccup doesn't kill the run."
      },
      "typeVersion": 1
    },
    {
      "id": "c34c2e84-6aa4-428c-929e-d1d49e10656e",
      "name": "Chat Trigger - Shopping Request",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "position": [
        7952,
        2336
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1.1
    },
    {
      "id": "968a79b1-0c7f-4039-bd21-e023862db4d5",
      "name": "Manual Trigger - Test Run",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        7952,
        2528
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "c5021d6d-2f69-4c3b-ba38-3076350e30fb",
      "name": "Set - Config",
      "type": "n8n-nodes-base.set",
      "position": [
        8176,
        2416
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "name": "apiBaseUrl",
              "type": "string",
              "value": "https://api.yourstore.com/v1/products"
            },
            {
              "name": "orderApiBaseUrl",
              "type": "string",
              "value": "https://api.yourstore.com/v1/orders"
            },
            {
              "name": "currency",
              "type": "string",
              "value": "USD"
            },
            {
              "name": "customerId",
              "type": "string",
              "value": "cust_12345"
            },
            {
              "name": "defaultShippingAddress",
              "type": "string",
              "value": "123 Main St, Springfield, USA"
            },
            {
              "name": "paymentMethodId",
              "type": "string",
              "value": "pm_default_card"
            },
            {
              "name": "maxBudget",
              "type": "number",
              "value": 500
            },
            {
              "name": "slackChannel",
              "type": "string",
              "value": "#shopping-assistant"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "3741731a-71e6-4203-b105-580805053238",
      "name": "HTTP Request Tool - Search Products API",
      "type": "n8n-nodes-base.httpRequestTool",
      "position": [
        8576,
        2624
      ],
      "parameters": {
        "url": "={{ $('Set - Config').item.json.apiBaseUrl }}",
        "options": {},
        "sendQuery": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "q",
              "value": "={{ $fromAI('query', 'search keywords for the product the user wants', 'string') }}"
            },
            {
              "name": "maxPrice",
              "value": "={{ $fromAI('maxPrice', 'maximum price in the configured currency, or empty for no limit', 'string') }}"
            },
            {
              "name": "category",
              "value": "={{ $fromAI('category', 'product category filter, or empty', 'string') }}"
            }
          ]
        },
        "toolDescription": "Search the product catalog. Use this whenever you need live product data: names, prices, stock, ratings. Provide a natural-language query and optionally a maxPrice and category to narrow results.",
        "nodeCredentialType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2,
      "continueOnFail": true
    },
    {
      "id": "13975f00-0fbf-47ca-a777-6a4c8d8d8c3b",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        8288,
        2624
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {},
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "0a9ec120-e7b0-4185-b0b3-4a5f5faf6305",
      "name": "Window Buffer Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        8432,
        2688
      ],
      "parameters": {
        "contextWindowLength": 10
      },
      "typeVersion": 1.3
    },
    {
      "id": "6ade2822-0c9e-4fdf-a40a-fce88e3987e6",
      "name": "AI Agent - Shopping Assistant",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        8432,
        2416
      ],
      "parameters": {
        "text": "=You are a helpful, budget-aware shopping assistant. Customer's currency: {{ $json.currency }}. Suggested max budget: {{ $json.maxBudget }}.\n\nUser message: {{ $json.chatInput }}\n\nUse the Search Products tool whenever you need live catalog data (price, stock, ratings) \u2014 never invent product details. Recommend at most 3 well-matched items when the user is browsing. Only set purchaseIntent to true once the user has clearly chosen a specific item and quantity to buy.\n\nAlways finish your turn with ONLY a JSON object (no markdown fences, no extra commentary) with keys: replyText (what to say to the user), shortlist (array of {productId, name, price, url}, empty if none), purchaseIntent (boolean), selectedProductId, selectedProductName, selectedProductPrice, quantity.",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 1.6
    },
    {
      "id": "ff085071-61f0-4dd4-ade3-42fec69de8d2",
      "name": "JS - Parse Agent Output & Build Shortlist",
      "type": "n8n-nodes-base.code",
      "position": [
        9136,
        2416
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Parse the shopping agent's structured JSON reply, falling back gracefully\nconst item = $input.item.json;\nconst raw = item.output || item.text || item.response || '{}';\n\nlet parsed = {};\ntry {\n  const cleaned = String(raw).replace(/```json|```/g, '').trim();\n  parsed = JSON.parse(cleaned);\n} catch (e) {\n  parsed = {\n    replyText: String(raw),\n    shortlist: [],\n    purchaseIntent: false,\n    selectedProductId: null,\n    selectedProductName: '',\n    selectedProductPrice: 0,\n    quantity: 1\n  };\n}\n\nconst config = $('Set - Config').item.json;\n\nreturn {\n  json: {\n    ...config,\n    replyText: parsed.replyText || '',\n    shortlist: parsed.shortlist || [],\n    purchaseIntent: !!parsed.purchaseIntent,\n    selectedProductId: parsed.selectedProductId || null,\n    selectedProductName: parsed.selectedProductName || '',\n    selectedProductPrice: parsed.selectedProductPrice || 0,\n    quantity: parsed.quantity || 1\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "ff909b78-a6fe-4b21-a695-d79951b2cb27",
      "name": "IF - Purchase Intent Detected",
      "type": "n8n-nodes-base.if",
      "position": [
        9376,
        2416
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.purchaseIntent }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "919d58a5-94d5-4c69-ab31-f201af41913c",
      "name": "Wait - For User Product Selection",
      "type": "n8n-nodes-base.wait",
      "position": [
        9616,
        2336
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "ab537208-8914-4626-bd94-f79a5009b587",
      "name": "IF - Selection Confirmed",
      "type": "n8n-nodes-base.if",
      "position": [
        9856,
        2336
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.selectionConfirmed === true || $json.selectionConfirmed === 'true' }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f702377b-31f5-4cf9-9b07-0c1f41fc3f6f",
      "name": "Wait - For Purchase Approval",
      "type": "n8n-nodes-base.wait",
      "position": [
        10096,
        2336
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "fac03496-b8ec-4f43-afd7-6fac4aac673b",
      "name": "IF - Purchase Approved",
      "type": "n8n-nodes-base.if",
      "position": [
        10336,
        2336
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.approved === true || $json.approved === 'true' }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "66108c75-375b-4f6f-b3e2-f1bbcf0a15bf",
      "name": "HTTP Request - Create Order (Checkout API)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        10576,
        2256
      ],
      "parameters": {
        "url": "={{ $('JS - Parse Agent Output & Build Shortlist').item.json.orderApiBaseUrl }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({ productId: $('JS - Parse Agent Output & Build Shortlist').item.json.selectedProductId, quantity: $('JS - Parse Agent Output & Build Shortlist').item.json.quantity, customerId: $('JS - Parse Agent Output & Build Shortlist').item.json.customerId, paymentMethodId: $('JS - Parse Agent Output & Build Shortlist').item.json.paymentMethodId, shippingAddress: $('JS - Parse Agent Output & Build Shortlist').item.json.defaultShippingAddress, currency: $('JS - Parse Agent Output & Build Shortlist').item.json.currency }) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2,
      "continueOnFail": true
    },
    {
      "id": "988b3737-30ee-48c5-9551-642657075881",
      "name": "Wait - For Order Confirmation Webhook",
      "type": "n8n-nodes-base.wait",
      "position": [
        10816,
        2256
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "3e678502-21cb-4390-95f9-e4769335b44c",
      "name": "JS - Parse Order Confirmation & Build Notification",
      "type": "n8n-nodes-base.code",
      "position": [
        11056,
        2256
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Turn the async order-confirmation callback into a clean notification\nconst webhookData = $input.item.json.body || $input.item.json;\nconst orderCtx = $('HTTP Request - Create Order (Checkout API)').item.json;\nconst cfg = $('JS - Parse Agent Output & Build Shortlist').item.json;\n\nconst orderId = webhookData.orderId || orderCtx.id || 'unknown';\nconst status = webhookData.status || orderCtx.status || 'pending';\nconst total = webhookData.total || (cfg.selectedProductPrice * cfg.quantity);\nconst currency = cfg.currency || 'USD';\nconst eta = webhookData.estimatedDelivery || 'TBD';\n\nconst message = 'Order ' + orderId + ' is ' + status + '. Total: ' + currency + ' ' + total + '. Estimated delivery: ' + eta + '. Item: ' + cfg.selectedProductName + ' x' + cfg.quantity + '.';\n\nreturn {\n  json: {\n    ...cfg,\n    orderId,\n    orderStatus: status,\n    orderTotal: total,\n    orderCurrency: currency,\n    estimatedDelivery: eta,\n    notificationMessage: message\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "f89f1eb7-baae-49df-b9c5-c6fc81666921",
      "name": "Slack - Notify Customer",
      "type": "n8n-nodes-base.slack",
      "position": [
        11296,
        2256
      ],
      "parameters": {
        "text": "=\ud83d\udecd\ufe0f *Order Confirmed*\n\n*Item:* {{ $json.selectedProductName }} x{{ $json.quantity }}\n*Order ID:* {{ $json.orderId }}\n*Status:* {{ $json.orderStatus }}\n*Total:* {{ $json.orderCurrency }} {{ $json.orderTotal }}\n*Estimated delivery:* {{ $json.estimatedDelivery }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $json.slackChannel }}"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3,
      "continueOnFail": true
    },
    {
      "id": "e4699dac-a1c3-4e24-8bad-907635645fb2",
      "name": "Set - Direct Answer Response",
      "type": "n8n-nodes-base.set",
      "position": [
        9376,
        2656
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "name": "finalMessage",
              "type": "string",
              "value": "={{ $json.replyText }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "55e14846-762f-4037-9e5c-51e97f8ca36f",
  "nodeGroups": [],
  "connections": {
    "Set - Config": {
      "main": [
        [
          {
            "node": "AI Agent - Shopping Assistant",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent - Shopping Assistant",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Window Buffer Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent - Shopping Assistant",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "IF - Purchase Approved": {
      "main": [
        [
          {
            "node": "HTTP Request - Create Order (Checkout API)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set - Direct Answer Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF - Selection Confirmed": {
      "main": [
        [
          {
            "node": "Wait - For Purchase Approval",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set - Direct Answer Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Trigger - Test Run": {
      "main": [
        [
          {
            "node": "Set - Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait - For Purchase Approval": {
      "main": [
        [
          {
            "node": "IF - Purchase Approved",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent - Shopping Assistant": {
      "main": [
        [
          {
            "node": "JS - Parse Agent Output & Build Shortlist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF - Purchase Intent Detected": {
      "main": [
        [
          {
            "node": "Wait - For User Product Selection",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set - Direct Answer Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chat Trigger - Shopping Request": {
      "main": [
        [
          {
            "node": "Set - Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait - For User Product Selection": {
      "main": [
        [
          {
            "node": "IF - Selection Confirmed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait - For Order Confirmation Webhook": {
      "main": [
        [
          {
            "node": "JS - Parse Order Confirmation & Build Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request Tool - Search Products API": {
      "ai_tool": [
        [
          {
            "node": "AI Agent - Shopping Assistant",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "JS - Parse Agent Output & Build Shortlist": {
      "main": [
        [
          {
            "node": "IF - Purchase Intent Detected",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request - Create Order (Checkout API)": {
      "main": [
        [
          {
            "node": "Wait - For Order Confirmation Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JS - Parse Order Confirmation & Build Notification": {
      "main": [
        [
          {
            "node": "Slack - Notify Customer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}