This workflow follows the Agent → OpenAI Embeddings recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "RAG Ecommerce Whatsapp",
"nodes": [
{
"parameters": {},
"type": "@devlikeapro/n8n-nodes-waha.wahaTrigger",
"typeVersion": 202502,
"position": [
420,
-400
],
"id": "7d96094f-d979-4c4f-8857-3c225a17c0d0",
"name": "WAHA Trigger",
"notesInFlow": false
},
{
"parameters": {
"resource": "Chatting",
"operation": "Stop Typing",
"session": "={{ $node[\"WAHA Trigger\"].json.session }}",
"chatId": "={{ $node[\"WAHA Trigger\"].json.payload.from }}",
"requestOptions": {}
},
"type": "@devlikeapro/n8n-nodes-waha.WAHA",
"typeVersion": 202502,
"position": [
1580,
60
],
"id": "c8b54a80-c158-4a68-b744-9b614678ab3f",
"name": "Stop Typing",
"alwaysOutputData": true,
"credentials": {
"wahaApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "Chatting",
"operation": "Send Text",
"session": "={{ $node[\"WAHA Trigger\"].json.session }}",
"chatId": "={{ $node[\"WAHA Trigger\"].json.payload.from }}",
"text": "={{ $node[\"AI Agent\"].json.output }}",
"requestOptions": {}
},
"type": "@devlikeapro/n8n-nodes-waha.WAHA",
"typeVersion": 202502,
"position": [
1740,
60
],
"id": "4ad3abcc-c4b8-425e-a0e2-fcf35713bd8d",
"name": "Send a text message",
"alwaysOutputData": true,
"credentials": {
"wahaApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"promptType": "define",
"text": "={{ $node[\"WAHA Trigger\"].json.payload.body }}",
"hasOutputParser": true,
"options": {
"systemMessage": "> \ud83e\udde0 **You are an intelligent and friendly AI assistant built to provide customer support via WhatsApp for an ecommerce platform.**\n>\n> Your primary role is to assist users in real time through WhatsApp with questions related to:\n>\n> * Orders and shipping\n> * Payments and refunds\n> * Product details\n> * Account support\n> * And other ecommerce topics\n>\n> \ud83d\udd27 **You are equipped with the following tools**:\n>\n> * **\u26a0\ufe0f [WARNING]** Before using any tools from the list, ensure that the customer is registered for tools that required it, and that customer consent is granted for tools that required it.\n> * **[WARNING]** Do not ask unecessary question like, can i have consent for viewing your cart detail? because viewing cart detail does not require consent permission, only ask this to the tools that needed it, this also applies to register status.\n>\n> * **[Answer Tool]** \u2013 A retrieval-augmented generation (RAG) system containing 50+ curated answers to frequently asked eCommerce questions. Use this as your only source of static knowledge.\n> * **[Get Customer]** \u2013 Retrieve a customer\u2019s name or basic information, you can use this tool to check if customer registered.\n> * **[Get Order / Shipping Status By Invoice]** \u2013 Check the real-time order and shipping status using the provided invoice ID, required customer to be registered.\n> * **[Search Available Products]** \u2013 Search products by name. Use this when customers ask to see specific products.\n> * **[Get Customer Orders]** \u2013 Get the 10 most recent orders placed by a customer, required customer to be registered.\n> * **[Get Stores]** \u2013 Use this to check if the customer owns a store or is a seller, required customer to be registered.\n> * **[Register Customer]** \u2013 Register a new customer.\n> * **[Update Customer Info]** \u2013 Update a customer\u2019s first or last name. Requires customer consent to be true and customer to be registered.\n> * **[Get Customer Cart]** \u2013 Retrieve products currently in the customer\u2019s cart, required customer to be registered.\n> * **[Add To Cart]** \u2013 Add product SKUs/variants to the customer\u2019s cart. Requires customer consent to be true and customer to be registered.\n> * **[Remove From Cart]** \u2013 Decrease quantity of product SKUs/variants in the customer\u2019s cart. Requires customer consent to be true and customer to be registered.\n> * **[Get Customer Consent]** \u2013 Get the customer's consent. You must not perform destructive actions if consent is false, required customer to be registered.\n> * **[Get Product Details]** \u2013 Get product details, including SKUs and variant attributes, using the product ID.\n> * **[Update Customer Consent]** \u2013 Update the customer's consent status. If false, the customer disallows destructive actions; if true, they allow them, required customer to be registered.\n>\n>\n> ---\n>\n> \u2705 **Your responsibilities**:\n>\n> * You can check if a customer registered or not by using **[Get Customer]** tool.\n> * Do not ask customers about their phone number because the system automatically retrieve this.\n> * If one of the tools have a problem or errors please tell the customer to try again later, this also applies when there are unexpected errors.\n> * Always use the **Get Customer** Tool when responding to customer inquiries.\nIf the user is not registered, use the **Answer Tool** to guide them on how to register an account.\n> * If the user has a store (as confirmed by the **Get Stores**), ask whether they would like to proceed as a seller or a customer.\n> * Use the **Answer Tool** to reply to all general inquiries. If a question closely matches an entry, return that answer clearly and confidently.\n> * For personalized queries (e.g., \u201cWhat\u2019s the status of my order with invoice ID 2?\u201d), use the **Get Customer Tool** to identify the user and the **Get Order / Shipping Status By Invoice** to retrieve tracking information.\n> * Always ground your answers in the tools above. Do **not** guess, fabricate, or improvise responses.\n>\n> \u274c If a relevant answer is **not found** in the **Answer Tool**, respond politely:\n>\n> > \"Thanks for your question. I\u2019ll forward this to our human support team for further assistance. We\u2019ll get back to you shortly.\"\n>\n> ---\n>\n> \u2728 **Tone and style (for WhatsApp)**:\n>\n> * Friendly and approachable, yet professional\n> * Use natural, conversational language that fits WhatsApp\n> * Keep messages short, clear, and helpful\n> * Use emojis sparingly and only when appropriate for clarity or warmth"
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2,
"position": [
1260,
60
],
"id": "3a98c498-2602-4f2b-8408-e256700b5182",
"name": "AI Agent",
"alwaysOutputData": true
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [
1100,
300
],
"id": "93aafb2e-18d4-4904-8967-3ee43b538113",
"name": "OpenAI Chat Model",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"typeVersion": 1.2,
"position": [
1140,
700
],
"id": "0352eea4-2d91-427c-8d96-258ae2b60e3b",
"name": "Embeddings OpenAI",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "Chatting",
"operation": "Start Typing",
"session": "={{ $node[\"WAHA Trigger\"].json.session }}",
"chatId": "={{ $node[\"WAHA Trigger\"].json.payload.from }}",
"requestOptions": {}
},
"type": "@devlikeapro/n8n-nodes-waha.WAHA",
"typeVersion": 202502,
"position": [
920,
60
],
"id": "a6fcaf45-7aa4-48e8-92da-414866473bf6",
"name": "Start Typing",
"alwaysOutputData": true,
"credentials": {
"wahaApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"sessionIdType": "customKey",
"sessionKey": "={{ $json.sessionId || \"Anonymous\" }}"
},
"type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
"typeVersion": 1.3,
"position": [
1240,
260
],
"id": "87c937f3-fa0a-4494-87ee-a856ae4c941a",
"name": "Postgres Chat Memory",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "Chatting",
"operation": "Send Seen",
"session": "={{ $node[\"WAHA Trigger\"].json.session }}",
"chatId": "={{ $node[\"WAHA Trigger\"].json.payload.from }}",
"messageId": "=",
"participant": "null",
"requestOptions": {}
},
"type": "@devlikeapro/n8n-nodes-waha.WAHA",
"typeVersion": 202502,
"position": [
760,
60
],
"id": "602958b8-06c3-483d-8087-a0a2e5a1d6e4",
"name": "Send Seen",
"alwaysOutputData": true,
"credentials": {
"wahaApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "for (const item of $(\"WAHA Trigger\").all()) {\n item.json.sessionId = item.json.payload.from\n}\n\nreturn $(\"WAHA Trigger\").all();"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1080,
60
],
"id": "19592222-0bb0-4896-a249-9e469031c71b",
"name": "Code"
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [
1420,
600
],
"id": "146454fe-feb5-4bd7-9601-08ed78117187",
"name": "OpenAI Chat Model1",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"qdrantCollection": {
"__rl": true,
"value": "marketplace_qna",
"mode": "id"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.vectorStoreQdrant",
"typeVersion": 1.3,
"position": [
1140,
560
],
"id": "e3b668aa-921d-4869-a7fd-a1736f34bb14",
"name": "Qdrant Vector Store",
"credentials": {
"qdrantApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"description": "**Use THIS TOOL to answer the user's question as accurately and clearly as possible.**\nThis tool is your **primary knowledge base**, containing **curated and verified information** about the ecommerce platform\u2019s support topics, including:\n\n* Order status, shipping, and delivery\n* Payment methods, refunds, and failed transactions\n* Return and exchange policies\n* Product availability, specs, and warranties\n* Account management and login issues\n* Promotions, discounts, and checkout support\n* Technical or site-related problems\n\n\u2705 Always refer to this tool **first** when answering any customer question.\n\u2705 If the user\u2019s query matches or closely relates to an entry, use the provided answer directly.\n\n\u274c **Do NOT guess or make up answers.**\n\u274c If the question is **not covered** in this tool, respond with:\n\n> \"Thank you for your question. I\u2019ll forward this to our human support team for further assistance. We\u2019ll get back to you shortly.\"\n\nThis ensures all responses are reliable, consistent, and based only on approved ecommerce support content."
},
"type": "@n8n/n8n-nodes-langchain.toolVectorStore",
"typeVersion": 1.1,
"position": [
1220,
420
],
"id": "c1759c72-aedb-4378-9fb1-1dda25855a82",
"name": "Answer Tool"
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use this tool to retrieve and interpret the current status of an order based on the following `marketplace.order_status` enum:\n\n| **Status** | **Description** |\n| ----------- | ---------------------------------------------------------------------------------------- |\n| `pending` | \ud83d\udd52 Your order has been received and is awaiting payment. |\n| `paid` | \ud83d\udce6 Your payment is confirmed. Your order is now being prepared for shipment. |\n| `shipped` | \ud83d\ude9a Your order has been shipped and is on its way to your address. |\n| `settled` | \u2705 Your order has been delivered successfully to your address. |\n| `cancelled` | \u274c Your order has been cancelled, either due to expiration or manual cancellation by you. |\n\nif you get the shipping status `cancelled` then you must also describe the status from the table above.\n\nTHIS TOOL need customer to be registered.",
"operation": "executeQuery",
"query": "SELECT mo.status order_status, ms.status shipping_status FROM marketplace.shippings ms JOIN marketplace.orders mo ON ms.order_id = mo.id JOIN marketplace.customers mc ON mc.id = mo.customer_id WHERE mo.invoice_id = $1 AND mc.phone_number = $2",
"options": {
"queryReplacement": "={{ `${$fromAI('Query_Parameters', `Please do no provide any additional info if the customer send INV-889 then just input INV-889`, 'string')},${$json.payload?.from?.split(\"@\")?.[0] ?? \"000\"}` }}",
"replaceEmptyStrings": true
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
1980,
380
],
"id": "17a372d2-f031-4a7e-8b5c-bd6dc86035a4",
"name": "Get Order / Shipping Status By Invoice",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to register a new customer.\nThe fields email and first_name are required.\nThe tool also accepts an optional last_name.",
"schema": {
"__rl": true,
"value": "marketplace",
"mode": "list",
"cachedResultName": "marketplace"
},
"table": {
"__rl": true,
"value": "customers",
"mode": "list",
"cachedResultName": "customers"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"phone_number": "={{ $json.sessionId?.split(\"@\")?.[0] }}",
"email": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('email', ``, 'string') }}",
"first_name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('first_name', ``, 'string') }}",
"last_name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('last_name', ``, 'string') }}",
"hashed_password": "sample_password"
},
"matchingColumns": [
"id"
],
"schema": [
{
"id": "id",
"displayName": "id",
"required": false,
"defaultMatch": true,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "phone_number",
"displayName": "phone_number",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "email",
"displayName": "email",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "first_name",
"displayName": "first_name",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "last_name",
"displayName": "last_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "hashed_password",
"displayName": "hashed_password",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "created_at",
"displayName": "created_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "updated_at",
"displayName": "updated_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true,
"removed": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
1800,
560
],
"id": "fdf716b4-45ab-403e-872e-ddda78377e4e",
"name": "Register Customer",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to update a customer's profile.\nThis tool can only update the first_name and last_name fields.\n\nTHIS TOOL Requires customer consent to be true and customer to be registered",
"operation": "update",
"schema": {
"__rl": true,
"value": "marketplace",
"mode": "list",
"cachedResultName": "marketplace"
},
"table": {
"__rl": true,
"value": "customers",
"mode": "list",
"cachedResultName": "customers"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"phone_number": "={{ $json.sessionId?.split(\"@\")?.[0] }}",
"first_name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('first_name', ``, 'string') }}",
"last_name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('last_name', ``, 'string') }}"
},
"matchingColumns": [
"phone_number"
],
"schema": [
{
"id": "id",
"displayName": "id",
"required": false,
"defaultMatch": true,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "phone_number",
"displayName": "phone_number",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "email",
"displayName": "email",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "first_name",
"displayName": "first_name",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "last_name",
"displayName": "last_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "hashed_password",
"displayName": "hashed_password",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "created_at",
"displayName": "created_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "updated_at",
"displayName": "updated_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true,
"removed": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
1980,
560
],
"id": "e81c29c5-aa5b-4226-a339-4880e2f240a3",
"name": "Update Customer Info",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to retrieve the latest 10 orders made by a customer.\nIt returns up to 10 of their most recent orders, sorted by placed_at in descending order.\n\nYou can also optionally filter orders placed after a specific date using the placed_at timestamp.\n\nTHIS TOOL need user to be registered.",
"operation": "executeQuery",
"query": "SELECT \n o.id AS order_id,\n o.invoice_id,\n o.total_amount,\n o.status,\n o.note,\n o.expires_ms,\n o.placed_at,\n o.updated_at,\n o.store_id,\n c.phone_number,\n c.first_name,\n c.last_name\nFROM \n marketplace.orders o\nJOIN \n marketplace.customers c ON c.id = o.customer_id\nWHERE \n c.phone_number = $1\n AND (NULLIF($2, 'nil') IS NULL OR o.placed_at >= NULLIF($2, 'nil')::timestamp)\nORDER BY \n o.placed_at DESC\nLIMIT 10;\n",
"options": {
"queryReplacement": "={{ (() => {\n const userId = $json.sessionId?.split(\"@\")?.[0] ?? '';\n const placedAt = $fromAI(\n 'placed_at_filter',\n `placed_at is an optional date filter used to retrieve only the orders placed after a specific timestamp.\n It should be in ISO 8601 format (e.g., 2024-01-01T00:00:00Z).\n \n If provided, only orders with a placed_at value greater than or equal to this date will be returned.\n If not provided (empty or null), all matching orders will be included regardless of date.`,\n 'string'\n );\n return [userId, placedAt || 'nil'];\n})() }}"
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
2300,
380
],
"id": "af9a5337-0d5c-4751-a3c1-d609a8fac13e",
"name": "Get Customer Orders",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to inquiry the customer information.",
"operation": "select",
"schema": {
"__rl": true,
"value": "marketplace",
"mode": "list",
"cachedResultName": "marketplace"
},
"table": {
"__rl": true,
"value": "customers",
"mode": "list",
"cachedResultName": "customers"
},
"limit": 1,
"where": {
"values": [
{
"column": "phone_number",
"value": "={{ $json.sessionId?.split(\"@\")?.[0] ?? \"000\" }}"
}
]
},
"options": {
"outputColumns": [
"first_name",
"last_name",
"created_at",
"updated_at",
"phone_number",
"email"
]
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
1620,
380
],
"id": "4a4db77b-e062-4b7c-b4df-df8be88dfca6",
"name": "Get Customer",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to retrieve all cart items.\nThe results include product variant details and are sorted by created_at in descending order (most recent items first).\n\nYou can also optionally filter the cart items to include only those added after a specific created_at timestamp.",
"operation": "executeQuery",
"query": "SELECT \n c.customer_id,\n c.variant_id,\n c.quantity,\n c.created_at,\n c.updated_at,\n pv.sku,\n pv.price,\n pv.stock,\n pv.image,\n p.name AS product_name\nFROM \n marketplace.carts c\nJOIN \n marketplace.product_variants pv ON pv.id = c.variant_id\nJOIN \n marketplace.products p ON p.id = pv.product_id\nJOIN\n marketplace.customers mc ON mc.id = c.customer_id\nWHERE \n mc.phone_number = $1\n AND (NULLIF($2, 'nil') IS NULL OR c.created_at >= NULLIF($2, 'nil')::timestamp)\nORDER BY \n c.created_at DESC;\n",
"options": {
"queryReplacement": "={{ (() => {\n const userId = $json.sessionId?.split(\"@\")?.[0] ?? '';\n const createdAt = $fromAI('created_at_filter', 'Can be optionally set, filter by product created date', 'string');\n return [userId, createdAt || 'nil'];\n})() }}"
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
2140,
560
],
"id": "f953b73e-4251-499c-8d14-8b0f5e2f9c25",
"name": "Get Customer Cart",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to add product variant into the cart it uses variant_id and quantity.\n\nThis tools need user is registered and user consent to be true.",
"operation": "executeQuery",
"query": "WITH customer AS (\n SELECT id AS customer_id\n FROM marketplace.customers\n WHERE phone_number = $1\n)\nINSERT INTO marketplace.carts (\n customer_id,\n variant_id,\n quantity\n)\nSELECT \n customer.customer_id,\n $2, -- variant_id\n $3 -- quantity\nFROM customer\nON CONFLICT (customer_id, variant_id)\nDO UPDATE SET\n quantity = marketplace.carts.quantity + EXCLUDED.quantity,\n updated_at = NOW() AT TIME ZONE 'UTC';\n",
"options": {
"queryReplacement": "={{ (() => {\n const userId = $json.sessionId?.split(\"@\")?.[0];\n const variantId = $fromAI(\n 'variant_id',\n `The ID of the product variant to be added to the cart. This uniquely identifies a specific variant (e.g., color, size) of a product.\n Example: 101 (represents the black, 64GB version of a phone)`,\n 'string'\n );\n const quantity = $fromAI(\n 'quantity',\n `The number of units to add to the cart for the specified variant.\n If the variant is already in the cart, the quantity will be incremented by this amount.\n Example: 2 (adds two more units)`,\n 'number'\n );\n return [userId, variantId, quantity];\n})() }}"
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
2300,
560
],
"id": "05619c68-930f-4088-acc4-970ce0ac576e",
"name": "Add To Cart",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to remove cart items or decrement the quantity.\nit uses product variant id and quantity\nThis tools need user is registered and user consent to be true.\n",
"operation": "executeQuery",
"query": "WITH customer AS (\n SELECT id AS customer_id\n FROM marketplace.customers\n WHERE phone_number = $1\n),\nupdated AS (\n UPDATE marketplace.carts\n SET quantity = quantity - $3,\n updated_at = NOW() AT TIME ZONE 'UTC'\n FROM customer\n WHERE \n marketplace.carts.customer_id = customer.customer_id\n AND variant_id = $2\n AND quantity > $3\n RETURNING marketplace.carts.*\n)\nDELETE FROM marketplace.carts\nWHERE \n customer_id = (SELECT customer_id FROM customer)\n AND variant_id = $2\n AND NOT EXISTS (\n SELECT 1 FROM updated WHERE updated.customer_id = marketplace.carts.customer_id AND updated.variant_id = marketplace.carts.variant_id\n );\n",
"options": {
"queryReplacement": "={{ (() => {\n return [\n $json.sessionId?.split(\"@\")?.[0],\n $fromAI(\n 'variant_id',\n `The ID of the product variant to be added to the cart. This uniquely identifies a specific variant (e.g., color, size) of a product. Example: 101 (represents the black, 64GB version of a phone)`,\n 'string'\n ),\n $fromAI(\n 'quantity',\n `The number of units to add to the cart for the specified variant. If the variant is already in the cart, the quantity will be incremented by this amount. Example: 2 (adds two more units)`,\n 'number'\n )\n ];\n})() }}"
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
1620,
720
],
"id": "a64cecee-7fd8-42ab-b0c9-566c77a10785",
"name": "Remove From Cart",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to check if a customer is a seller and see their stores.\n\nTHIS TOOL need user to be registered.",
"operation": "executeQuery",
"query": "SELECT * FROM marketplace.stores ms JOIN marketplace.customers mc ON mc.id = ms.owner_id WHERE mc.phone_number = $1",
"options": {
"queryReplacement": "={{ $json.sessionId?.split(\"@\")?.[0] }}"
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
1620,
560
],
"id": "a43155b7-d992-4b57-bd56-8cf8624348a4",
"name": "Get Stores",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
2300,
720
],
"id": "9d659ad3-b066-4977-a2e9-5cbb011f2371",
"name": "Get Store Courier Option",
"credentials": {
"postgres": {
"name": "<your credential>"
}
},
"disabled": true
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to search for products based on their names. The search is case-insensitive and supports partial matches using ILIKE.",
"operation": "executeQuery",
"query": "SELECT \n p.id AS product_id,\n p.name AS product_name,\n p.description,\n p.image AS product_image,\n p.created_at\nFROM \n marketplace.products p\nWHERE \n p.name ILIKE '%' || $1 || '%'\nORDER BY \n p.created_at DESC\nLIMIT 10;\n",
"options": {
"queryReplacement": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Query_Parameters', `If the user says something like \u201cI want reviews for product KZ ZSN Pro,\u201d you should use the name exactly as provided.`, 'string') }}"
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
2140,
380
],
"id": "a9a0ac60-7ee4-4da3-8c36-e0ab0635c8ba",
"name": "Search Available Products",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to retrieve complete information about a product using its product ID.\n\nThe result includes:\n\n - Basic product information (name, description, image, timestamps)\n\n - All available variants (SKU, price, stock, availability, image)\n\n - All associated variant attributes and their values (e.g., color, size) for each variant\n\nThis tool requires:\n\n - id (the unique identifier of the product)",
"operation": "executeQuery",
"query": "WITH product_data AS (\n SELECT \n p.id AS product_id,\n p.name AS product_name,\n p.description,\n p.image AS product_image,\n p.created_at AS product_created_at,\n p.updated_at AS product_updated_at\n FROM marketplace.products p\n WHERE p.id = $1\n),\nvariants AS (\n SELECT \n v.id AS variant_id,\n v.product_id,\n v.sku,\n v.price,\n v.stock,\n v.image AS variant_image,\n v.is_available,\n v.created_at AS variant_created_at,\n v.updated_at AS variant_updated_at\n FROM marketplace.product_variants v\n WHERE v.product_id = $1\n),\nattribute_map AS (\n SELECT\n pav.id AS attribute_value_id,\n pav.\"value\" AS attribute_value,\n va.\"name\" AS attribute_name,\n pvv.variant_id\n FROM marketplace.product_variant_value pvv\n JOIN marketplace.variant_attribute_value pav ON pav.id = pvv.attribute_value_id\n JOIN marketplace.variant_attributes va ON va.id = pav.attribute_id\n WHERE va.product_id = $1\n)\nSELECT \n pd.product_id,\n pd.product_name,\n pd.description,\n pd.product_image,\n pd.product_created_at,\n pd.product_updated_at,\n v.variant_id,\n v.sku,\n v.price,\n v.stock,\n v.variant_image,\n v.is_available,\n v.variant_created_at,\n v.variant_updated_at,\n am.attribute_name,\n am.attribute_value\nFROM product_data pd\nJOIN variants v ON v.product_id = pd.product_id\nLEFT JOIN attribute_map am ON am.variant_id = v.variant_id\nORDER BY v.variant_created_at DESC, am.attribute_name, am.attribute_value;\n",
"options": {
"queryReplacement": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Query_Parameters', `The product id not their name, it should be number`, 'string') }}"
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
1980,
720
],
"id": "951a4b51-eb5c-4ede-947f-bae7ec234a01",
"name": "Get Product Details",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
2140,
720
],
"id": "024229ef-b14b-4cff-81cf-1831afdf05ea",
"name": "Place Order",
"credentials": {
"postgres": {
"name": "<your credential>"
}
},
"disabled": true
},
{
"parameters": {
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
1800,
880
],
"id": "475c442c-f07e-47e8-8e83-dfee6ea9be36",
"name": "Get Shipping Histories",
"credentials": {
"postgres": {
"name": "<your credential>"
}
},
"disabled": true
},
{
"parameters": {
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
1620,
880
],
"id": "38b3ad77-09fb-490e-8d41-a52be80c499d",
"name": "Submit Product Review",
"credentials": {
"postgres": {
"name": "<your credential>"
}
},
"disabled": true
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to get product reviews based on the product name. This TOOL uses an ILIKE query.\n\u2013 If there are two top results with the same name but from different stores, you should ask the customer whether they want to see reviews from Store A or Store B.\n\u2013 If there are duplicate results with exactly the same name and from the same store, you should ask the customer which product they mean by ID.",
"operation": "executeQuery",
"query": "SELECT \n mr.rating, \n mr.comment, \n mr.attachments, \n CONCAT(mc.first_name, ' ', mc.last_name) AS full_name, \n mp.name AS product_name\nFROM \n marketplace.reviews mr\nJOIN \n marketplace.customers mc ON mc.id = mr.customer_id\nJOIN \n marketplace.products mp ON mp.id = mr.product_id\nJOIN \n marketplace.stores ms ON ms.id = mp.store_id\nWHERE \n mp.name ILIKE '%' || $1 || '%'",
"options": {
"queryReplacement": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Query_Parameters', `If the user says something like \u201cI want reviews for product KZ ZSN Pro,\u201d you should use the name exactly as provided.`, 'string') }}"
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
1800,
380
],
"id": "c9c8758f-7b47-428b-8ec2-6c64c2870af6",
"name": "Get Product Reviews",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to check whether the user has given consent for the AI to perform critical actions such as INSERT, UPDATE, or DELETE.\n\nBy default, the AI must not perform these actions unless enable_ai_actions is explicitly set to true.\nIf enable_ai_actions is false or not provided, the AI must avoid executing any potentially destructive operations.\n\nTHIS TOOL need user to be registered.",
"operation": "select",
"schema": {
"__rl": true,
"value": "marketplace",
"mode": "list",
"cachedResultName": "marketplace"
},
"table": {
"__rl": true,
"value": "customers",
"mode": "list",
"cachedResultName": "customers"
},
"limit": 1,
"where": {
"values": [
{
"column": "phone_number",
"value": "={{ $json.sessionId?.split(\"@\")?.[0] }}"
}
]
},
"options": {
"outputColumns": [
"enable_ai_actions"
]
}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
1800,
720
],
"id": "81870e36-9004-46c6-9212-42abdc18ba45",
"name": "Get Consumer Consent",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use THIS TOOL to update the customer's consent preferences, specifically the enable_ai_actions flag.\n\nTHIS TOOL need customer to be registered.\n\nThis flag determines whether the AI is allowed to perform critical actions such as INSERT, UPDATE, or DELETE on behalf of the customer.\n\n - Set enable_ai_actions to true to allow such actions.\n\n - Set it to false to restrict the AI from performing them.",
"operation": "update",
"schema": {
"__rl": true,
"value": "marketplace",
"mode": "list",
"cachedResultName": "marketplace"
},
"table": {
"__rl": true,
"value": "customers",
"mode": "list",
"cachedResultName": "customers"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"enable_ai_actions": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('enable_ai_actions', ``, 'boolean') }}",
"phone_number": "={{ $json.sessionId?.split(\"@\")?.[0] }}"
},
"matchingColumns": [
"phone_number"
],
"schema": [
{
"id": "id",
"displayName": "id",
"required": false,
"defaultMatch": true,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "phone_number",
"displayName": "phone_number",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "email",
"displayName": "email",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "first_name",
"displayName": "first_name",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "last_name",
"displayName": "last_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "hashed_password",
"displayName": "hashed_password",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "created_at",
"displayName": "created_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "updated_at",
"displayName": "updated_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "enable_ai_actions",
"displayName": "enable_ai_actions",
"required": false,
"defaultMatch": false,
"display": true,
"type": "boolean",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgresTool",
"typeVersion": 2.6,
"position": [
1980,
880
],
"id": "9b2861bc-d3bd-4d3b-94a9-5c3c984a2392",
"name": "Update Customer Consent",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
}
],
"connections": {
"WAHA Trigger": {
"main": [
[],
[
{
"node": "Send Seen",
"type": "main",
"index": 0
}
]
]
},
"Stop Typing": {
"main": [
[
{
"node": "Send a text message",
"type": "main",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Stop Typing",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Embeddings OpenAI": {
"ai_embedding": [
[
{
"node": "Qdrant Vector Store",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Start Typing": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"Postgres Chat Memory": {
"ai_memory": [
[
{
"node": "AI Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"Send Seen": {
"main": [
[
{
"node": "Start Typing",
"type": "main",
"index": 0
}
]
]
},
"Code": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model1": {
"ai_languageModel": [
[
{
"node": "Answer Tool",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Qdrant Vector Store": {
"ai_vectorStore": [
[
{
"node": "Answer Tool",
"type": "ai_vectorStore",
"index": 0
}
]
]
},
"Answer Tool": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get Order / Shipping Status By Invoice": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Register Customer": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Update Customer Info": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get Customer Orders": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get Customer": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get Customer Cart": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Add To Cart": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Remove From Cart": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get Stores": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get Store Courier Option": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Search Available Products": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get Product Details": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Place Order": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get Shipping Histories": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Submit Product Review": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get Product Reviews": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get Consumer Consent": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Update Customer Consent": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"timezone": "Asia/Jakarta",
"callerPolicy": "workflowsFromSameOwner"
},
"versionId": "d03b7a87-5d0a-416f-9bc5-4802ce8bb00a",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "WV1gdtr8bOFOk0Qa",
"tags": []
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
openAiApipostgresqdrantApiwahaApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
RAG Ecommerce Whatsapp. Uses @devlikeapro/n8n-nodes-waha, agent, lmChatOpenAi, embeddingsOpenAi. Event-driven trigger; 31 nodes.
Source: https://github.com/abdurrahmanharitsghiffary/waha-n8n-rag/blob/main/workflows/ecommerce_rag_whatsapp_V2.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
RAG Ecommerce Whatsapp. Uses @devlikeapro/n8n-nodes-waha, agent, lmChatOpenAi, embeddingsOpenAi. Event-driven trigger; 31 nodes.
RAG Whatsapp. Uses @devlikeapro/n8n-nodes-waha, agent, lmChatOpenAi, embeddingsOpenAi. Event-driven trigger; 13 nodes.
Alfred (funcional). Uses gmailTool, googleCalendarTool, gmail, embeddingsOpenAi. Event-driven trigger; 83 nodes.
Your AI workforce is ready. Are you?
Automate Outreach Prospect automates finding, enriching, and messaging potential partners (like restaurants, malls, and bars) using Apify Google Maps scraping, Perplexity enrichment, OpenAI LLMs, Goog