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 →
{
"createdAt": "2025-10-11T19:27:07.787Z",
"updatedAt": "2025-10-25T07:25:06.000Z",
"id": "J9fS2IqweuJi5M9j",
"name": "Vendors",
"active": true,
"isArchived": false,
"nodes": [
{
"parameters": {
"operation": "executeQuery",
"query": "UPDATE vendors SET\n vendor_name = '{{ $json.body.vendor_name }}',\n website = '{{ $json.body.website }}',\n contact_email = '{{ $json.body.contact_email }}',\n phone_number = '{{ $json.body.phone_number }}',\n vendor_icon_name = '{{ $json.body.vendor_icon_name }}',\n vendor_icon_type = '{{ $json.body.vendor_icon_type }}',\n vendor_icon_content = decode('{{ $json.body.vendor_icon_content }}', 'base64')\nWHERE vendor_id = {{ $json.body.vendor_id }}\nRETURNING *;\n",
"options": {}
},
"name": "Execute Vendor Update",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
160,
608
],
"id": "51063782-6a83-4a76-8018-dad13196eecc",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const data = $input.first().json;\nreturn [{ json: { success: true, message: 'Vendor updated', data } }];"
},
"name": "Format Vendor Update Response",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
384,
608
],
"id": "2f1ea46c-efe9-4e1f-bb7a-3a36a30c84d8"
},
{
"parameters": {
"respondWith": "allIncomingItems",
"options": {}
},
"name": "Respond Vendor Update",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
608,
608
],
"id": "4fe4cd5f-0332-47d8-85a9-bb81a105d888"
},
{
"parameters": {
"jsCode": "// Get all incoming items\nconst items = $input.all();\n\n// Extract query from the first item, or use an empty object\nconst q = (items[0] && items[0].json && items[0].json.query) ? items[0].json.query : {};\n\n// Safely parse and default parameters\nconst search = (q.search || \" \").trim(); // Ensure it's always a string\nconst limit = Number.isInteger(parseInt(q.pageSize)) ? parseInt(q.pageSize) : 100000;\nconst pageNo = Number.isInteger(parseInt(q.pageNo)) ? parseInt(q.pageNo) : 1;\nconst offset = (pageNo - 1) * limit;\n\n// Return a single item with params for the next node\nreturn [\n {\n json: {\n params: {\n search,\n limit,\n offset\n }\n }\n }\n];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
160,
928
],
"id": "eba15563-b8fe-4dda-b2cb-02e8fe474ee8",
"name": "Parse Params4"
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT \n *\nFROM public.vendors\nWHERE \n vendor_name ILIKE '%{{ $json.params.search }}%'\n OR contact_email ILIKE '%{{ $json.params.search }}%'\n OR website ILIKE '%{{ $json.params.search }}%'\nORDER BY vendor_name ASC \nLIMIT {{ $json.params.limit }} \nOFFSET {{ $json.params.offset }}\n",
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
384,
928
],
"id": "bf08b130-6525-41b5-be5c-0830b3d2c027",
"name": "Execute a SQL query4",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"respondWith": "allIncomingItems",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
832,
832
],
"id": "eaec72b9-b865-413c-a254-487138a04fd2",
"name": "Respond to Webhook4"
},
{
"parameters": {
"jsCode": "return $input.all().map(item => {\n const data = item.json;\n const imageBuffer = Buffer.from(data.vendor_icon_content.data);\n const imageType = data.vendor_icon_type || 'jpeg';\n const base64Image = imageBuffer.toString('base64');\n\n // Generate filename\n const filename = `vendor_${data.vendor_id}.${imageType}`;\n\n return {\n json: { // 'data' is the standard expected binary property name\n data: base64Image,\n mimeType: `image/${imageType}`,\n fileName: filename\n }\n };\n});\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
608,
1024
],
"id": "d567fe1c-c251-4c4c-8a79-5070b267e2d8",
"name": "Image2"
},
{
"parameters": {
"operation": "toBinary",
"sourceProperty": "data",
"options": {
"fileName": "={{ $json.fileName }}",
"mimeType": "={{ $json.mimeType }}"
}
},
"type": "n8n-nodes-base.convertToFile",
"typeVersion": 1.1,
"position": [
832,
1024
],
"id": "0a1a27a7-49ba-4bc2-a579-38224eba4832",
"name": "Convert to File2"
},
{
"parameters": {
"operation": "write",
"fileName": "=/images/vendors/{{ $('Image2').item.json.fileName }}",
"options": {}
},
"type": "n8n-nodes-base.readWriteFile",
"typeVersion": 1,
"position": [
1056,
1024
],
"id": "b6a2180e-bb4d-4c71-ab3a-035f29681f2d",
"name": "Read/Write Files from Disk2"
},
{
"parameters": {
"jsCode": "const keysToRemove = ['vendor_icon_content', 'vendor_icon_type'];\n\nreturn $input.all().map(item => {\n const json = {...item.json};\n keysToRemove.forEach(k => delete json[k]);\n \n json.filename = `http://192.168.0.7:5678/webhook/image?filename=vendor_${item.json.vendor_id}.${item.json.vendor_icon_type || 'jpeg'}`;\n\n return { json };\n});\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
608,
832
],
"id": "8560ed4e-bc10-4f3b-935a-d9c0ea523ea6",
"name": "Image3"
},
{
"parameters": {
"path": "api/components/vendors",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-64,
928
],
"id": "002f2181-c7b2-446b-a100-bbb9415ddd15",
"name": "api/components/vendors"
},
{
"parameters": {
"httpMethod": "POST",
"path": "api/components/vendors/update",
"responseMode": "responseNode",
"options": {}
},
"name": "UPDATE api/components/vendors/:id",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-64,
608
],
"id": "ceaf0646-5243-49f2-b688-dc7328a10cad"
},
{
"parameters": {
"jsCode": "// Get incoming data from POST body\nconst items = $input.all();\nconst body = (items[0] && items[0].json && items[0].json.body) ? items[0].json.body : {};\n\n// Extract vendor ID from body\nconst vendorId = parseInt(body.vendor_id || body.vendorId || body.id) || 0;\n\n// Validate\nif (!vendorId) {\n throw new Error('Vendor ID is required in request body');\n}\n\nreturn [\n {\n json: {\n params: {\n id: vendorId\n }\n }\n }\n];"
},
"name": "Parse Vendor Delete Params",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
160,
384
],
"id": "ef82a62c-0ef6-4525-a546-482f4f6b9f72"
},
{
"parameters": {
"operation": "executeQuery",
"query": "DELETE FROM vendors WHERE vendor_id = {{ $json.params.id }} RETURNING *;",
"options": {}
},
"name": "Execute Vendor Delete",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
384,
384
],
"id": "c9acc4c5-f08d-4cef-a25d-a9559cdedb5f",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const result = $input.first().json;\nif (result && result.vendor_id) {\n return [{ json: { success: true, message: 'Vendor deleted successfully' } }];\n}\nreturn [{ json: { success: false, message: 'Vendor not found or could not be deleted' } }];"
},
"name": "Format Vendor Delete Response",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
608,
384
],
"id": "efa66b1f-ee25-4ab6-801b-f618532a709b"
},
{
"parameters": {
"respondWith": "allIncomingItems",
"options": {}
},
"name": "Respond Vendor Delete",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
832,
384
],
"id": "a64fdca3-84ad-49e8-8d2d-03613b349fc7"
},
{
"parameters": {
"httpMethod": "POST",
"path": "api/components/vendors/delete",
"responseMode": "responseNode",
"options": {}
},
"name": "DELETE api/components/vendors/delete",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-64,
384
],
"id": "a7b9ecd0-6b26-43d5-901d-4785a6991422"
},
{
"parameters": {
"path": "api/components/vendors/meta",
"responseMode": "responseNode",
"options": {}
},
"name": "api/components/vendors/meta",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-64,
160
],
"id": "e8a5121c-2b17-4019-b30c-fb9ec9d55d7d"
},
{
"parameters": {
"operation": "executeQuery",
"query": "WITH vendor_stats AS (\n SELECT \n COUNT(DISTINCT v.vendor_id) as total_vendors,\n COUNT(CASE WHEN v.vendor_icon_name IS NOT NULL THEN 1 END) as vendors_with_icon,\n COUNT(CASE WHEN v.website IS NOT NULL AND v.website != '' THEN 1 END) as vendors_with_website,\n COUNT(CASE WHEN v.contact_email IS NOT NULL AND v.contact_email != '' THEN 1 END) as vendors_with_email\n FROM vendors v\n),\norder_stats AS (\n SELECT\n COUNT(*) as total_orders,\n SUM(total_cost) as total_spent,\n MAX(order_date) as last_order_date\n FROM orders\n),\ntop_vendor AS (\n SELECT\n v.vendor_id,\n v.vendor_name,\n COUNT(o.order_id) as order_count,\n SUM(o.total_cost) as total_spent,\n CASE \n WHEN v.vendor_icon_name IS NOT NULL \n THEN CONCAT('http://192.168.0.7:5678/webhook/image?filename=', v.vendor_icon_name)\n ELSE NULL\n END as vendor_icon\n FROM vendors v\n LEFT JOIN orders o ON v.vendor_id = o.vendor_id\n GROUP BY v.vendor_id, v.vendor_name, v.vendor_icon_name\n ORDER BY order_count DESC, total_spent DESC\n LIMIT 1\n),\nrecent_purchase AS (\n SELECT\n v.vendor_name,\n o.order_date,\n o.total_cost,\n CASE \n WHEN v.vendor_icon_name IS NOT NULL \n THEN CONCAT('http://192.168.0.7:5678/webhook/image?filename=', v.vendor_icon_name)\n ELSE NULL\n END as vendor_icon\n FROM orders o\n JOIN vendors v ON o.vendor_id = v.vendor_id\n ORDER BY o.order_date DESC\n LIMIT 1\n)\nSELECT \n (SELECT row_to_json(vendor_stats.*) FROM vendor_stats) as vendor_stats,\n (SELECT row_to_json(order_stats.*) FROM order_stats) as order_stats,\n (SELECT row_to_json(top_vendor.*) FROM top_vendor) as top_vendor,\n (SELECT row_to_json(recent_purchase.*) FROM recent_purchase) as recent_purchase;",
"options": {}
},
"name": "Execute Vendors Meta Query",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
160,
160
],
"id": "26ab9533-7fa1-4200-b064-aa336005aa41",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const data = $input.first().json;\nconst vendorStats = data.vendor_stats || {};\nconst orderStats = data.order_stats || {};\nconst topVendor = data.top_vendor || {};\nconst recentPurchase = data.recent_purchase || {};\n\nconst total = parseInt(vendorStats.total_vendors || 0);\nconst withIcon = parseInt(vendorStats.vendors_with_icon || 0);\nconst withWebsite = parseInt(vendorStats.vendors_with_website || 0);\nconst withEmail = parseInt(vendorStats.vendors_with_email || 0);\n\nreturn [{\n json: {\n summary: {\n totalVendors: total,\n vendorsWithIcon: withIcon,\n vendorsWithWebsite: withWebsite,\n vendorsWithEmail: withEmail\n },\n completeness: {\n iconCompletion: total > 0 ? Math.round((withIcon / total) * 100) : 0,\n websiteCompletion: total > 0 ? Math.round((withWebsite / total) * 100) : 0,\n emailCompletion: total > 0 ? Math.round((withEmail / total) * 100) : 0\n },\n purchases: {\n totalOrders: parseInt(orderStats.total_orders || 0),\n totalSpent: parseFloat(orderStats.total_spent || 0),\n lastOrderDate: orderStats.last_order_date || null\n },\n topVendor: {\n vendorId: topVendor.vendor_id || null,\n vendorName: topVendor.vendor_name || null,\n orderCount: parseInt(topVendor.order_count || 0),\n totalSpent: parseFloat(topVendor.total_spent || 0),\n vendorIcon: topVendor.vendor_icon || null\n },\n recentPurchase: {\n vendorName: recentPurchase.vendor_name || null,\n orderDate: recentPurchase.order_date || null,\n totalCost: parseFloat(recentPurchase.total_cost || 0),\n vendorIcon: recentPurchase.vendor_icon || null\n }\n }\n}];"
},
"name": "Format Vendors Meta",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
384,
160
],
"id": "41afe7f2-af4f-4d80-97c1-3792d9534f2f"
},
{
"parameters": {
"respondWith": "allIncomingItems",
"options": {}
},
"name": "Respond Vendors Meta",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
608,
160
],
"id": "b7052ab5-3e04-4b01-82c3-749f1484df13"
},
{
"parameters": {
"httpMethod": "POST",
"path": "api/components/vendors",
"responseMode": "responseNode",
"options": {}
},
"name": "ADD api/components/vendors",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-64,
-32
],
"id": "de48b8f8-70fd-4981-b814-db7341a84816"
},
{
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO public.vendors (\n vendor_name,\n website,\n contact_email,\n phone_number,\n vendor_icon_name,\n vendor_icon_type,\n vendor_icon_content\n)\nVALUES (\n '{{ $json.body.vendor_name }}',\n '{{ $json.body.website }}',\n '{{ $json.body.contact_email }}',\n '{{ $json.body.phone_number }}',\n '{{ $json.body.vendor_icon_name }}',\n '{{ $json.body.vendor_icon_type }}',\n decode('{{ $json.body.vendor_icon_content }}', 'base64')\n)\nRETURNING \n vendor_id,\n vendor_name,\n website,\n contact_email,\n phone_number,\n vendor_icon_name,\n vendor_icon_type,\n vendor_icon_content,\n CASE \n WHEN vendor_icon_name IS NOT NULL \n THEN CONCAT('http://192.168.0.7:5678/webhook/image?filename=', vendor_icon_name)\n ELSE NULL\n END as filename;",
"options": {}
},
"name": "Execute Vendor Insert",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2,
"position": [
160,
-32
],
"id": "241e4c02-7411-4797-85d9-0ccfb4a46fec",
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const data = $input.first().json;\nreturn [{ \n json: { \n success: true, \n message: 'Vendor created successfully', \n vendor: data \n } \n}];"
},
"name": "Format Vendor Insert Response",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
384,
-32
],
"id": "0b31d9bc-6ad8-475d-a3a3-550a4280e043"
},
{
"parameters": {
"respondWith": "allIncomingItems",
"options": {}
},
"name": "Respond Vendor Create",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
608,
-32
],
"id": "8c19a118-6f3b-4948-8cb0-149dcf1d0c02"
}
],
"connections": {
"Execute Vendor Update": {
"main": [
[
{
"node": "Format Vendor Update Response",
"type": "main",
"index": 0
}
]
]
},
"Format Vendor Update Response": {
"main": [
[
{
"node": "Respond Vendor Update",
"type": "main",
"index": 0
}
]
]
},
"Parse Params4": {
"main": [
[
{
"node": "Execute a SQL query4",
"type": "main",
"index": 0
}
]
]
},
"Execute a SQL query4": {
"main": [
[
{
"node": "Image3",
"type": "main",
"index": 0
},
{
"node": "Image2",
"type": "main",
"index": 0
}
]
]
},
"Image2": {
"main": [
[
{
"node": "Convert to File2",
"type": "main",
"index": 0
}
]
]
},
"Convert to File2": {
"main": [
[
{
"node": "Read/Write Files from Disk2",
"type": "main",
"index": 0
}
]
]
},
"Image3": {
"main": [
[
{
"node": "Respond to Webhook4",
"type": "main",
"index": 0
}
]
]
},
"api/components/vendors": {
"main": [
[
{
"node": "Parse Params4",
"type": "main",
"index": 0
}
]
]
},
"UPDATE api/components/vendors/:id": {
"main": [
[
{
"node": "Execute Vendor Update",
"type": "main",
"index": 0
}
]
]
},
"Parse Vendor Delete Params": {
"main": [
[
{
"node": "Execute Vendor Delete",
"type": "main",
"index": 0
}
]
]
},
"Execute Vendor Delete": {
"main": [
[
{
"node": "Format Vendor Delete Response",
"type": "main",
"index": 0
}
]
]
},
"Format Vendor Delete Response": {
"main": [
[
{
"node": "Respond Vendor Delete",
"type": "main",
"index": 0
}
]
]
},
"DELETE api/components/vendors/delete": {
"main": [
[
{
"node": "Parse Vendor Delete Params",
"type": "main",
"index": 0
}
]
]
},
"api/components/vendors/meta": {
"main": [
[
{
"node": "Execute Vendors Meta Query",
"type": "main",
"index": 0
}
]
]
},
"Execute Vendors Meta Query": {
"main": [
[
{
"node": "Format Vendors Meta",
"type": "main",
"index": 0
}
]
]
},
"Format Vendors Meta": {
"main": [
[
{
"node": "Respond Vendors Meta",
"type": "main",
"index": 0
}
]
]
},
"ADD api/components/vendors": {
"main": [
[
{
"node": "Execute Vendor Insert",
"type": "main",
"index": 0
}
]
]
},
"Execute Vendor Insert": {
"main": [
[
{
"node": "Format Vendor Insert Response",
"type": "main",
"index": 0
}
]
]
},
"Format Vendor Insert Response": {
"main": [
[
{
"node": "Respond Vendor Create",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"meta": null,
"versionId": "df784ab9-5e77-463e-b40c-15c668cfd57a",
"triggerCount": 5,
"tags": [],
"shared": [
{
"createdAt": "2025-10-11T19:27:07.794Z",
"updatedAt": "2025-10-11T19:27:07.794Z",
"role": "workflow:owner",
"workflowId": "J9fS2IqweuJi5M9j",
"projectId": "0tBJbgcFWwxEMKPn",
"project": {
"createdAt": "2025-10-05T16:55:31.619Z",
"updatedAt": "2025-10-05T16:55:58.616Z",
"id": "0tBJbgcFWwxEMKPn",
"name": "Bikash Panda <oksbwn@gmail.com>",
"type": "personal",
"icon": null,
"description": null
}
}
]
}
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.
postgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Vendors. Uses postgres, readWriteFile. Webhook trigger; 25 nodes.
Source: https://github.com/oksbwn/Inventory-Management-System/blob/7f26462a83a574049df178775808226e970cd4f8/n8n/J9fS2IqweuJi5M9j.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.
Boxes APIs. Uses postgres, readWriteFile. Webhook trigger; 28 nodes.
n8n-supabase-ai-pipeline. Uses supabase, httpRequest, postgres, readWriteFile. Webhook trigger; 25 nodes.
CMM. Uses httpRequest, postgres, redis. Webhook trigger; 90 nodes.
Scraping. Uses httpRequest, postgres, @apify/n8n-nodes-apify, respondToWebhook. Webhook trigger; 61 nodes.