Whatsapp CRM powered by Baserow
Shared 8/18/2025
149 views
Visual Workflow
JSON Code
{
"meta": {
"instanceId": "834bc6c387a1c56d0622a24b912577f9e6d66c5873f4e6426166054eb488d8fc",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "965e5063-60cc-46ef-a6af-0739aa721a39",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2660,
-860
],
"parameters": {
"width": 420,
"height": 960,
"content": "### **WhatsApp Communication Hub**\n\nAutomate WhatsApp communication and client management without the complexities of the official WhatsApp Business API. This workflow uses **WasenderAPI** to streamline the process, centralizing your CRM by processing, organizing, and logging all client interactions into **Baserow**.\n\n**How it works**\n* **Receives Messages:** A single webhook listens for all inbound and outbound messages.\n* **Prepares Data:** A custom JavaScript node standardizes the data, converting phone numbers, and identifying the message type (`text`, `image`, `video`, etc.), and whether it's a reply to a previous message.\n* **Routes the Flow:** The workflow intelligently routes messages based on their direction (`fromMe` to outbound, `not fromMe` to inbound) and then by message type.\n* **Manages Contacts:** It first checks if the sender is a new or existing contact. For new contacts, it creates a new record and fetches their profile picture. For existing ones, it simply updates their last activity timestamp.\n* **Logs Conversations:** All conversations are logged into Baserow. The workflow handles text messages, media files (which are decrypted via WasenderAPI), and even the content of quoted messages.\n\n**Setup Steps**\nSetup is quick (under 5 min). Simply connect WasenderAPI webhooks to n8n, duplicate Baserow 'Contacts' ([link](https://baserow.io/public/grid/a5iWkAQpu8QljUlgwgm_pour_Au5BKd3mtkfu-B6N7Y)) and 'Messages' ([link](https://baserow.io/public/grid/0H22XZitFDWnrVNnKwBfiI7M6XX5CugHrXHEzdCY4xY)) table templates, and add API credentials in n8n.\n\n**Requirements**\n* Active n8n instance (self-hosted/cloud).\n* WasenderAPI.com trial/subscription.\n* Baserow account.\n\n**How to customize the workflow**\nYou can customize Baserow IDs, the decryption process, and the logic for data extraction and message content.\n\n**Note:** The flow layout is structured to handle messages in the correct order. Please keep the flow layout as is to ensure proper functioning."
},
"typeVersion": 1
},
{
"id": "cf54f3da-4ea7-47b7-95de-a1ba187aeed6",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1020,
-840
],
"parameters": {
"color": 7,
"width": 820,
"content": "## 1. Manage Incoming Contacts\n\n* **Existing Contact:** Update last activity timestamp.\n* **New Contact:** Create a new contact record using the WhatsApp number."
},
"typeVersion": 1
},
{
"id": "04a820b4-65ce-4211-b8b4-458310da3efe",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-600,
-460
],
"parameters": {
"color": 7,
"width": 700,
"height": 200,
"content": "## Optional: Contact Profile Picture\n\nThis feature fetches the WhatsApp profile picture for contacts. It is entirely optional and can be removed if not required. When enabled, this data enhances visual contact management, such as in a Baserow gallery view: [Baserow Gallery View Example](https://baserow.io/public/gallery/v7PfThVQLOIc6moc9j0Ebjq_Az4Nm3TMMDzpvF30Esk)."
},
"typeVersion": 1
},
{
"id": "4c1b1c35-61d6-46d9-b4f1-b052200e23b7",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1120,
-160
],
"parameters": {
"color": 7,
"width": 1160,
"height": 140,
"content": "## 2. Message Type Detection & Image Handling\n\nFor images, messages are first decrypted via WasenderAPI. Decrypted images are then uploaded and mapped directly to Baserow's image fields, ensuring visual content storage.\n\n\n\n\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "89baf636-e406-4474-bb37-5977937a230c",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1000,
280
],
"parameters": {
"color": 7,
"width": 680,
"height": 140,
"content": "## 3. We only log a text message here \n\nTHE IF node is to check if the message comes from your connected wasenderapi number "
},
"typeVersion": 1
},
{
"id": "5b471655-8405-47b6-ad8e-0fb4837ff39a",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2160,
-860
],
"parameters": {
"color": 6,
"width": 380,
"height": 240,
"content": "## Important Note\n\nFor all message notifications (inbound/outbound), you *must* select the `messages.upsert` event.\n\n### Recommendation\n\nImplement header authentication using \"x-webhook-signature\"."
},
"typeVersion": 1
},
{
"id": "1cb9af04-cb13-4c2c-a6ef-c305de1d9898",
"name": "Baserow: Search Contact",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1480,
-560
],
"parameters": {
"url": "https://api.baserow.io/api/database/rows/table/630465/",
"options": {},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"queryParameters": {
"parameters": [
{
"name": "user_field_names",
"value": "true"
},
{
"name": "search",
"value": "={{ $json.readable_phone_number }}"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"id": "bIuRYRo4bDDwywzn",
"name": "Header Auth Baserow"
}
},
"typeVersion": 4.2
},
{
"id": "a52f3f3d-552b-4bed-9234-e482311d189c",
"name": "Route: Contact Exists?",
"type": "n8n-nodes-base.switch",
"position": [
-1260,
-560
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "New_Contact",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "e930a9f3-ad63-4c99-bff4-931ddbfe1bca",
"operator": {
"type": "number",
"operation": "equals"
},
"leftValue": "={{ $json.count }}",
"rightValue": 0
}
]
},
"renameOutput": true
},
{
"outputKey": "Existing_Contact",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "6071cc1e-70ba-4072-bfcb-6c56aa3ecb4e",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.count }}",
"rightValue": 0
}
]
},
"renameOutput": true
}
]
},
"options": {},
"looseTypeValidation": true
},
"typeVersion": 3.2
},
{
"id": "bc311d87-a829-4138-b075-7d0be75bfd89",
"name": "Create New Contact",
"type": "n8n-nodes-base.baserow",
"position": [
-820,
-660
],
"parameters": {
"tableId": 622539,
"fieldsUi": {
"fieldValues": [
{
"fieldId": 5064505,
"fieldValue": "={{ $('Prepare: Standardize Incoming Data').item.json.readable_phone_number }}"
},
{
"fieldId": 5064506,
"fieldValue": "={{ $('Prepare: Standardize Incoming Data').item.json.body.data.messages.pushName }}"
},
{
"fieldId": 5064507,
"fieldValue": "New"
},
{
"fieldId": 5064508,
"fieldValue": "={{ $('Prepare: Standardize Incoming Data').item.json.body.timestamp.toDateTime('ms')}}"
}
]
},
"operation": "create",
"databaseId": 264981
},
"credentials": {
"baserowApi": {
"id": "HadM27Hzjj2CaqeH",
"name": "Baserow account"
}
},
"typeVersion": 1
},
{
"id": "883c65e9-c300-438b-ad88-63a7c1c7d9cf",
"name": "WasenderAPI: Fetch Profile Picture URL",
"type": "n8n-nodes-base.httpRequest",
"position": [
-600,
-660
],
"parameters": {
"url": "=https://www.wasenderapi.com/api/contacts/{{ $('Prepare: Standardize Incoming Data').item.json.body.data.messages.key.remoteJid }}/picture",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"id": "8wTjfLPGuZTRsRDi",
"name": "whatsapp_0620150096"
}
},
"typeVersion": 4.2
},
{
"id": "e17b823f-b41f-4339-ad03-d799321a2496",
"name": "Baserow: Upload Profile Picture File",
"type": "n8n-nodes-base.httpRequest",
"position": [
-380,
-660
],
"parameters": {
"url": "https://api.baserow.io/api/user-files/upload-via-url/",
"method": "POST",
"options": {},
"jsonBody": "={\n \"url\": \"{{ $json.data.imgUrl}}\"\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"id": "bIuRYRo4bDDwywzn",
"name": "Header Auth Baserow"
}
},
"typeVersion": 4.2
},
{
"id": "bdcbea48-7d5e-4f36-a2b6-cffd2ca239d6",
"name": "Update Contact Profile Picture Field",
"type": "n8n-nodes-base.baserow",
"position": [
-160,
-660
],
"parameters": {
"rowId": "={{ $('Create New Contact').item.json.id }}",
"tableId": 622539,
"fieldsUi": {
"fieldValues": [
{
"fieldId": 5065051,
"fieldValue": "={{ $json.name }}"
}
]
},
"operation": "update",
"databaseId": 264981
},
"credentials": {
"baserowApi": {
"id": "HadM27Hzjj2CaqeH",
"name": "Baserow account"
}
},
"typeVersion": 1
},
{
"id": "a810d63a-8ce7-41b5-9fb6-889780ed2f20",
"name": "Route: Message Type (Image/Text)",
"type": "n8n-nodes-base.switch",
"position": [
-1340,
100
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "has_image",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "b865b469-58bd-4cfa-b359-bb2dbeb11134",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.is_media }}",
"rightValue": ""
}
]
},
"renameOutput": true
},
{
"outputKey": "txt_only",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "abc80fe6-2034-4918-8738-2cc3ce93cd03",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{ $json.is_media }}",
"rightValue": "webhook.test"
}
]
},
"renameOutput": true
}
]
},
"options": {},
"looseTypeValidation": true
},
"typeVersion": 3.2
},
{
"id": "d9ab2605-fba4-4a89-8a2a-4bc3eb3b8ec5",
"name": "Extract: Image Message Data",
"type": "n8n-nodes-base.set",
"position": [
-1120,
0
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "08f9b688-dc42-4724-b9e3-5969215f69f2",
"name": "media_details",
"type": "object",
"value": "={{ $json.media_details }}"
},
{
"id": "ca2ab33b-ab78-4615-9052-f7dfafe9d20d",
"name": "message_id",
"type": "string",
"value": "={{ $json.body.data.messages.key.id }}"
},
{
"id": "cd93ec84-ccb1-4944-bee9-ef360bb7df8d",
"name": "remoteJid (wasenderapi)",
"type": "string",
"value": "={{ $json.body.data.messages.key.remoteJid }}"
},
{
"id": "a25f071c-70a6-45aa-b0fe-0c3256d41039",
"name": "fromMe (for filtering purposes)",
"type": "boolean",
"value": "={{ $json.body.data.messages.key.fromMe }}"
},
{
"id": "a09926b2-52ab-44ce-ba6c-29a58c90d99a",
"name": "readable_phone_number",
"type": "string",
"value": "={{ $json.readable_phone_number }}"
},
{
"id": "662eaeaf-56dd-4429-a8b9-279013dd2e14",
"name": "is_media",
"type": "boolean",
"value": "={{ $json.is_media }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "ce430aa0-48e0-46ef-a08a-a4ed1a874937",
"name": "WasenderAPI: Decrypt Image Outbound",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1200,
-1240
],
"parameters": {
"url": "https://www.wasenderapi.com/api/decrypt-media",
"method": "POST",
"options": {},
"jsonBody": "={\n \"data\": {\n \"messages\": {\n \"key\": {\n \"id\": \"{{ $json.body.data.messages.key.id }}\"\n },\n \"message\": {\n \"imageMessage\": {\n \"url\": \"{{ $json.media_details.url }}\",\n \"mimetype\": \"{{ $json.media_details.mimetype }}\",\n \"mediaKey\": \"{{ $json.media_details.mediaKey }}\"\n }\n }\n }\n }\n} ",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"id": "8wTjfLPGuZTRsRDi",
"name": "whatsapp_0620150096"
}
},
"typeVersion": 4.2
},
{
"id": "10f09ea0-3e56-4934-bfdb-67159ee303c7",
"name": "WasenderAPI: Decrypt Image Inbound",
"type": "n8n-nodes-base.httpRequest",
"position": [
-900,
0
],
"parameters": {
"url": "https://www.wasenderapi.com/api/decrypt-media",
"method": "POST",
"options": {},
"jsonBody": "={\n \"data\": {\n \"messages\": {\n \"key\": {\n \"id\": \"{{ $json.message_id }}\"\n },\n \"message\": {\n \"imageMessage\": {\n \"url\": \"{{ $json.media_details.url }}\",\n \"mimetype\": \"{{ $json.media_details.mimetype }}\",\n \"mediaKey\": \"{{ $json.media_details.mediaKey }}\",\n \"fileName\": \"{{ $json.message_id }}.{{ $json.media_details.mimetype.slice(6)}}\"\n }\n }\n }\n }\n} ",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"id": "8wTjfLPGuZTRsRDi",
"name": "whatsapp_0620150096"
}
},
"typeVersion": 4.2
},
{
"id": "dc5f8987-771e-443d-bead-c8476ec35fcf",
"name": "Baserow Upload Message Image",
"type": "n8n-nodes-base.httpRequest",
"position": [
-980,
-1240
],
"parameters": {
"url": "https://api.baserow.io/api/user-files/upload-via-url/",
"method": "POST",
"options": {},
"jsonBody": "={\n \"url\": \"{{ $json.publicUrl }}\"\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"id": "bIuRYRo4bDDwywzn",
"name": "Header Auth Baserow"
}
},
"typeVersion": 4.2
},
{
"id": "1221b883-190e-4db1-aa42-e53393c3cfab",
"name": "Baserow_Upload Message Image",
"type": "n8n-nodes-base.httpRequest",
"position": [
-680,
0
],
"parameters": {
"url": "https://api.baserow.io/api/user-files/upload-via-url/",
"method": "POST",
"options": {},
"jsonBody": "={\n \"url\": \"{{ $json.publicUrl }}\"\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"id": "bIuRYRo4bDDwywzn",
"name": "Header Auth Baserow"
}
},
"typeVersion": 4.2
},
{
"id": "eb5e01d6-b7ad-4135-976e-7359bafb9cda",
"name": "Log Inbound Image Message",
"type": "n8n-nodes-base.baserow",
"position": [
-460,
0
],
"parameters": {
"tableId": 622533,
"fieldsUi": {
"fieldValues": [
{
"fieldId": 5064439,
"fieldValue": "={{ $('Extract: Image Message Data').item.json.message_id }}"
},
{
"fieldId": 5064440,
"fieldValue": "={{ $('Extract: Image Message Data').item.json.readable_phone_number }}"
},
{
"fieldId": 5065335,
"fieldValue": "={{ $json.name }}"
},
{
"fieldId": 5064444,
"fieldValue": "Unread"
},
{
"fieldId": 5064441,
"fieldValue": "Inbound"
},
{
"fieldId": 5064443,
"fieldValue": "={{ $json.uploaded_at.toDateTime()}}"
},
{
"fieldId": 5064442,
"fieldValue": "={{ $('From_ME?').item.json.media_details.caption || \"no_caption\" }}"
}
]
},
"operation": "create",
"databaseId": 264981
},
"credentials": {
"baserowApi": {
"id": "HadM27Hzjj2CaqeH",
"name": "Baserow account"
}
},
"typeVersion": 1
},
{
"id": "82a56627-47cf-46c7-b20a-2c1f4fddbd96",
"name": "Log Outbound Image Message",
"type": "n8n-nodes-base.baserow",
"position": [
-760,
-1240
],
"parameters": {
"tableId": 622533,
"fieldsUi": {
"fieldValues": [
{
"fieldId": 5064439,
"fieldValue": "={{ $('Route: Message Type (outbound)').item.json.body.data.messages.key.id }}"
},
{
"fieldId": 5064440,
"fieldValue": "={{ $('Route: Message Type (outbound)').item.json.readable_phone_number }}"
},
{
"fieldId": 5065335,
"fieldValue": "={{ $json.name }}"
},
{
"fieldId": 5064444,
"fieldValue": "Unread"
},
{
"fieldId": 5064441,
"fieldValue": "Outbound"
},
{
"fieldId": 5064443,
"fieldValue": "={{ $('Route: Message Type (outbound)').item.json.body.timestamp.toDateTime('ms').toISO()}}"
},
{
"fieldId": 5064442,
"fieldValue": "={{ $('Route: Message Type (outbound)').item.json.media_details.caption || $('Route: Message Type (outbound)').item.json.message_text|| \"no_caption\"}}"
}
]
},
"operation": "create",
"databaseId": 264981
},
"credentials": {
"baserowApi": {
"id": "HadM27Hzjj2CaqeH",
"name": "Baserow account"
}
},
"typeVersion": 1
},
{
"id": "27823430-a6e9-4c73-a666-1581dd7b3c6f",
"name": "Extract: Text Message Data",
"type": "n8n-nodes-base.set",
"position": [
-980,
500
],
"parameters": {
"options": {
"ignoreConversionErrors": true
},
"assignments": {
"assignments": [
{
"id": "ca2ab33b-ab78-4615-9052-f7dfafe9d20d",
"name": "message_id",
"type": "string",
"value": "={{ $json.body.data.messages.key.id }}"
},
{
"id": "cd93ec84-ccb1-4944-bee9-ef360bb7df8d",
"name": "remoteJid (wasenderapi)",
"type": "string",
"value": "={{ $json.body.data.messages.key.remoteJid }}"
},
{
"id": "a09926b2-52ab-44ce-ba6c-29a58c90d99a",
"name": "readable_phone_number",
"type": "string",
"value": "={{ $json.readable_phone_number }}"
},
{
"id": "78a15c52-893d-44d9-9e3f-a826581e4751",
"name": "message_text",
"type": "string",
"value": "={{ $json.message_text }}"
},
{
"id": "5d3c035b-f933-4213-8e78-ec946c32470c",
"name": "quoted_message",
"type": "string",
"value": "={{ $json.quoted_message }}"
},
{
"id": "d579811e-a766-4bf4-9344-3adf1a354628",
"name": "message_type",
"type": "string",
"value": "={{ $json.message_type }}"
},
{
"id": "dcbdf4c2-e848-4a20-bc03-9ad4c9bbfd32",
"name": "timestamp",
"type": "number",
"value": "={{ $json.body.timestamp.toDateTime('ms').toISO()}}"
},
{
"id": "0f0c1bd2-d629-4052-be66-9bad542a1267",
"name": "is_replyTo",
"type": "boolean",
"value": "={{ $json.is_replyTo }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "fb013652-956e-4899-8ca7-63443cfbbb27",
"name": "Log Outbound Text Message",
"type": "n8n-nodes-base.baserow",
"position": [
-1200,
-1040
],
"parameters": {
"tableId": 622533,
"fieldsUi": {
"fieldValues": [
{
"fieldId": 5064439,
"fieldValue": "={{ $json.body.data.messages.key.id }}"
},
{
"fieldId": 5064440,
"fieldValue": "={{ $json.readable_phone_number }}"
},
{
"fieldId": 5064444,
"fieldValue": "Unread"
},
{
"fieldId": 5064441,
"fieldValue": "Outbound"
},
{
"fieldId": 5064443,
"fieldValue": "={{ $json.body.timestamp.toDateTime('ms').toISO()}}"
},
{
"fieldId": 5064442,
"fieldValue": "={{ $json.message_text ||\"failed\" }}"
}
]
},
"operation": "create",
"databaseId": 264981
},
"credentials": {
"baserowApi": {
"id": "HadM27Hzjj2CaqeH",
"name": "Baserow account"
}
},
"typeVersion": 1
},
{
"id": "1e060af3-c5d5-4466-a021-a5ed502c5e84",
"name": "Log Inbound Text Message",
"type": "n8n-nodes-base.baserow",
"position": [
-460,
660
],
"parameters": {
"tableId": 630472,
"fieldsUi": {
"fieldValues": [
{
"fieldId": 5139217,
"fieldValue": "={{ $json.message_id }}"
},
{
"fieldId": 5139219,
"fieldValue": "={{ $json.readable_phone_number }}"
},
{
"fieldId": 5139223,
"fieldValue": "Unread"
},
{
"fieldId": 5139220,
"fieldValue": "Inbound"
},
{
"fieldId": 5139222,
"fieldValue": "={{ $json.timestamp }}"
},
{
"fieldId": 5139221,
"fieldValue": "={{ $json.message_text }}"
}
]
},
"operation": "create",
"databaseId": 268764
},
"credentials": {
"baserowApi": {
"id": "HadM27Hzjj2CaqeH",
"name": "Baserow account"
}
},
"typeVersion": 1
},
{
"id": "942490d1-0db9-4514-821f-0f1a636c20ba",
"name": "Trigger: WhatsApp Message",
"type": "n8n-nodes-base.webhook",
"position": [
-2140,
-560
],
"webhookId": "9218c291-18b1-44a6-9b1c-4df4ff7605f6",
"parameters": {
"path": "upsert",
"options": {},
"httpMethod": "POST",
"authentication": "headerAuth"
},
"credentials": {
"httpHeaderAuth": {
"id": "sFH79l1dvIvxEu2W",
"name": "whatsapp_hook"
}
},
"typeVersion": 2
},
{
"id": "72bb657f-a9e4-4106-b7a4-70777b9ff20d",
"name": "Prepare: Standardize Incoming Data",
"type": "n8n-nodes-base.code",
"position": [
-1980,
-560
],
"parameters": {
"jsCode": "// Function to convert a WhatsApp JID to a readable phone number\nconst jidToReadableNumber = (jid) => {\n if (jid && jid.includes('@s.whatsapp.net')) {\n const numberPart = jid.split('@')[0];\n return `+${numberPart}`;\n }\n return jid;\n};\n\n// Function to get the text content of a message, including quoted messages\nconst getMessageText = (messageObject) => {\n let quotedMessage = null;\n let isReply = false;\n let text = null;\n\n // Check for quoted message\n if (messageObject.extendedTextMessage?.contextInfo?.quotedMessage) {\n const quoted = messageObject.extendedTextMessage.contextInfo.quotedMessage;\n isReply = true;\n // Check for text content in the quoted message\n if (quoted.extendedTextMessage?.text) {\n quotedMessage = quoted.extendedTextMessage.text;\n } else if (quoted.conversation) {\n quotedMessage = quoted.conversation;\n }\n }\n\n // Get the current message text\n if (messageObject.extendedTextMessage?.text) {\n text = messageObject.extendedTextMessage.text;\n } else if (messageObject.conversation) {\n text = messageObject.conversation;\n }\n\n return {\n text: text,\n quotedMessage: quotedMessage,\n isReplyTo: isReply\n };\n};\n\n// Function to check for a media message and extract its details\nconst handleMediaMessage = (messageObject) => {\n let mediaInfo = null;\n let mediaType = null;\n let quotedMedia = null;\n let isReply = false;\n\n // Check for quoted message\n if (messageObject.extendedTextMessage?.contextInfo?.quotedMessage) {\n isReply = true;\n }\n \n if (messageObject.imageMessage) {\n mediaInfo = messageObject.imageMessage;\n mediaType = 'image';\n } else if (messageObject.videoMessage) {\n mediaInfo = messageObject.videoMessage;\n mediaType = 'video';\n } else if (messageObject.audioMessage) {\n mediaInfo = messageObject.audioMessage;\n mediaType = 'audio';\n } else if (messageObject.documentMessage) {\n mediaInfo = messageObject.documentMessage;\n mediaType = 'document';\n }\n\n if (mediaInfo) {\n return {\n type: mediaType,\n url: mediaInfo.url,\n mediaKey: mediaInfo.mediaKey,\n mimetype: mediaInfo.mimetype,\n caption: mediaInfo.caption || '',\n filename: mediaInfo.fileName || '',\n isReplyTo: isReply\n };\n }\n\n return null;\n};\n\n// Main loop to process each input item\nfor (const item of $input.all()) {\n const whatsappFullPayload = item.json;\n const webhookBody = whatsappFullPayload.body;\n const messageObject = webhookBody?.data?.messages?.message || {};\n\n const remoteJid = webhookBody?.data?.messages?.remoteJid;\n item.json.readable_phone_number = jidToReadableNumber(remoteJid);\n item.json.fromMe = webhookBody?.data?.messages?.key?.fromMe;\n\n const messageContent = getMessageText(messageObject);\n const mediaDetails = handleMediaMessage(messageObject);\n\n if (mediaDetails) {\n item.json.is_media = true;\n item.json.message_type = 'media';\n item.json.media_details = mediaDetails;\n item.json.message_text = null;\n item.json.quoted_message = messageContent.quotedMessage;\n item.json.is_replyTo = mediaDetails.isReplyTo;\n } else if (messageContent.text) {\n item.json.is_media = false;\n item.json.message_type = 'text';\n item.json.message_text = messageContent.text;\n item.json.quoted_message = messageContent.quotedMessage;\n item.json.media_details = null;\n item.json.is_replyTo = messageContent.isReplyTo;\n } else {\n // Handle unhandled or empty messages\n item.json.is_media = false;\n item.json.message_type = 'other';\n item.json.message_text = null;\n item.json.quoted_message = null;\n item.json.media_details = null;\n item.json.is_replyTo = false;\n }\n}\n\nreturn $input.all();"
},
"typeVersion": 2
},
{
"id": "82f60dbc-cc02-4eed-895c-ad56372f52ad",
"name": "Check_Direction",
"type": "n8n-nodes-base.switch",
"position": [
-1820,
-560
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "out",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "289597c2-0f10-4e66-a12f-0635a896ef45",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.body.data.messages.key.fromMe }}",
"rightValue": ""
}
]
},
"renameOutput": true
},
{
"outputKey": "in",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "ec102869-a06b-479a-8d0a-489609ccb7d2",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{ $json.body.data.messages.key.fromMe }}",
"rightValue": ""
}
]
},
"renameOutput": true
}
]
},
"options": {}
},
"typeVersion": 3.2
},
{
"id": "f8bb9a6b-5309-4f76-85b7-b888aea9d7c9",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
-760,
500
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "552a970b-7a31-4908-9c8d-11ef46fe4ca6",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.is_replyTo }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "0e93edbc-23be-4343-8368-781fe141a032",
"name": "Log Inbound ReplyTo",
"type": "n8n-nodes-base.baserow",
"position": [
-460,
460
],
"parameters": {
"tableId": 622533,
"fieldsUi": {
"fieldValues": [
{
"fieldId": 5064439,
"fieldValue": "={{ $json.message_id }}"
},
{
"fieldId": 5064440,
"fieldValue": "={{ $json.readable_phone_number }}"
},
{
"fieldId": 5064444,
"fieldValue": "Unread"
},
{
"fieldId": 5064441,
"fieldValue": "Inbound"
},
{
"fieldId": 5064443,
"fieldValue": "={{ $json.timestamp }}"
},
{
"fieldId": 5064442,
"fieldValue": "={{ $json.is_replyTo ? 'Reply to: \"' + $json.quoted_message + '\"\\n\\n' + $json.message_text : $json.message_text }}"
},
{
"fieldId": 5064444,
"fieldValue": "ReplyTo"
}
]
},
"operation": "create",
"databaseId": 264981
},
"credentials": {
"baserowApi": {
"id": "HadM27Hzjj2CaqeH",
"name": "Baserow account"
}
},
"typeVersion": 1
},
{
"id": "7b6cd9fd-2ddb-4b63-9e89-2507094b025e",
"name": "Route: Message Type (outbound)",
"type": "n8n-nodes-base.switch",
"position": [
-1420,
-1140
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "has_image",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "b865b469-58bd-4cfa-b359-bb2dbeb11134",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.is_media }}",
"rightValue": ""
}
]
},
"renameOutput": true
},
{
"outputKey": "txt_only",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "abc80fe6-2034-4918-8738-2cc3ce93cd03",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{ $json.is_media }}",
"rightValue": "webhook.test"
}
]
},
"renameOutput": true
}
]
},
"options": {},
"looseTypeValidation": true
},
"typeVersion": 3.2
},
{
"id": "c510ae84-0c25-4d01-90a2-28511cc5f05e",
"name": "No Operation, do nothing1",
"type": "n8n-nodes-base.noOp",
"position": [
-840,
-460
],
"parameters": {},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"If": {
"main": [
[
{
"node": "Log Inbound ReplyTo",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Inbound Text Message",
"type": "main",
"index": 0
}
]
]
},
"Check_Direction": {
"main": [
[
{
"node": "Route: Message Type (outbound)",
"type": "main",
"index": 0
}
],
[
{
"node": "Baserow: Search Contact",
"type": "main",
"index": 0
},
{
"node": "Route: Message Type (Image/Text)",
"type": "main",
"index": 0
}
]
]
},
"Create New Contact": {
"main": [
[
{
"node": "WasenderAPI: Fetch Profile Picture URL",
"type": "main",
"index": 0
}
]
]
},
"Route: Contact Exists?": {
"main": [
[
{
"node": "Create New Contact",
"type": "main",
"index": 0
}
],
[
{
"node": "No Operation, do nothing1",
"type": "main",
"index": 0
}
]
]
},
"Baserow: Search Contact": {
"main": [
[
{
"node": "Route: Contact Exists?",
"type": "main",
"index": 0
}
]
]
},
"No Operation, do nothing1": {
"main": [
[]
]
},
"Trigger: WhatsApp Message": {
"main": [
[
{
"node": "Prepare: Standardize Incoming Data",
"type": "main",
"index": 0
}
]
]
},
"Extract: Text Message Data": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Extract: Image Message Data": {
"main": [
[
{
"node": "WasenderAPI: Decrypt Image Inbound",
"type": "main",
"index": 0
}
]
]
},
"Baserow Upload Message Image": {
"main": [
[
{
"node": "Log Outbound Image Message",
"type": "main",
"index": 0
}
]
]
},
"Baserow_Upload Message Image": {
"main": [
[
{
"node": "Log Inbound Image Message",
"type": "main",
"index": 0
}
]
]
},
"Route: Message Type (outbound)": {
"main": [
[
{
"node": "WasenderAPI: Decrypt Image Outbound",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Outbound Text Message",
"type": "main",
"index": 0
}
]
]
},
"Route: Message Type (Image/Text)": {
"main": [
[
{
"node": "Extract: Image Message Data",
"type": "main",
"index": 0
}
],
[
{
"node": "Extract: Text Message Data",
"type": "main",
"index": 0
}
]
]
},
"Prepare: Standardize Incoming Data": {
"main": [
[
{
"node": "Check_Direction",
"type": "main",
"index": 0
}
]
]
},
"WasenderAPI: Decrypt Image Inbound": {
"main": [
[
{
"node": "Baserow_Upload Message Image",
"type": "main",
"index": 0
}
]
]
},
"WasenderAPI: Decrypt Image Outbound": {
"main": [
[
{
"node": "Baserow Upload Message Image",
"type": "main",
"index": 0
}
]
]
},
"Baserow: Upload Profile Picture File": {
"main": [
[
{
"node": "Update Contact Profile Picture Field",
"type": "main",
"index": 0
}
]
]
},
"WasenderAPI: Fetch Profile Picture URL": {
"main": [
[
{
"node": "Baserow: Upload Profile Picture File",
"type": "main",
"index": 0
}
]
]
}
}
}