{ "name": "Polar.sh → CRM Sync", "nodes": [ { "id": "webhook-trigger", "name": "Polar Webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 2, "position": [250, 300], "webhookId": "polar-crm-sync", "authentication": "none", "properties": { "httpMethod": "POST", "path": "polar-webhook", "responseMode": "onReceived", "responseData": "", "options": {} }, "notes": "SETUP: 1) Deploy this workflow. 2) Copy the webhook URL. 3) Go to Polar.sh Dashboard → Settings → Webhooks → Add Endpoint. 4) Paste URL. 5) Select events: checkout.created, checkout.succeeded" }, { "id": "switch-crm", "name": "Route to CRM", "type": "n8n-nodes-base.switch", "typeVersion": 2, "position": [450, 300], "properties": { "dataType": "string", "value1": "", "value2": "", "outputType": "route", "rules": [ { "value": "", "outputKey": "Mautic" }, { "value": "", "outputKey": "Twenty CRM" } ] }, "notes": "SET ME: Change routing based on which CRM you're using. Currently routes to Mautic (output 1). Switch to Twenty by changing routing logic." }, { "id": "code-parse", "name": "Parse Polar Payload", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [650, 150], "properties": { "language": "javaScript", "code": "const payload = $input.first().json;\nconst eventType = payload.type || '';\nconst checkout = payload.data || {};\n\nconst email = checkout.customer?.email || checkout.customerEmail || '';\nconst name = checkout.customer?.name || checkout.customerName || '';\nconst amount = checkout.amount || 0;\nconst currency = checkout.currency || 'usd';\nconst productName = checkout.product?.name || checkout.productName || '';\nconst metadata = checkout.product?.metadata || checkout.metadata || {};\nconst donationType = metadata.donationType || metadata.type || '';\nconst timestamp = checkout.createdAt || new Date().toISOString();\n\nlet donationLabel = '';\nif (donationType === 'sadaqah' || productName.toLowerCase().includes('sadaqah')) {\n donationLabel = 'Sadaqah Jariyah';\n} else if (donationType === 'zakat' || productName.toLowerCase().includes('zakat')) {\n donationLabel = 'Zakat (Fi Sabilillah)';\n} else if (donationType === 'hibah' || productName.toLowerCase().includes('hibah')) {\n donationLabel = 'Hibah (Gift)';\n} else {\n donationLabel = 'Donation';\n}\n\nconst amountDisplay = currency === 'myr'\n ? `RM${(amount / 100).toFixed(2)}`\n : `${(amount / 100).toFixed(2)} ${currency.toUpperCase()}`;\n\nreturn {\n email,\n name,\n amount,\n amountDisplay,\n currency,\n donationLabel,\n donationType,\n productName,\n timestamp\n};" }, "notes": "Extracts fields from Polar.sh webhook payload." }, { "id": "mautic-upsert", "name": "Mautic - Upsert Contact", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [850, 100], "credentials": { "httpRequest": { "id": "SET_ME_MauticCredId", "name": "Mautic API" } }, "properties": { "method": "POST", "url": "https://mautic.falahos.my/api/contacts/new", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "sendBody": true, "bodyParameters": { "parameters": [ { "name": "email", "value": "={{ $json.email }}" }, { "name": "firstname", "value": "={{ $json.name.split(' ')[0] }}" }, { "name": "lastname", "value": "={{ $json.name.split(' ').slice(1).join(' ') }}" }, { "name": "tags", "value": "={{ 'polar-donor,' + $json.donationLabel.toLowerCase().replace(' ', '-') }}" } ] }, "options": {} }, "notes": "Creates/updates contact in Mautic CRM. Mautic is running at mautic.falahos.my. API credentials: admin / FalahMautic2026!" }, { "id": "mautic-note", "name": "Mautic - Add Donation Note", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [850, 300], "credentials": { "httpRequest": { "id": "SET_ME_MauticCredId", "name": "Mautic API" } }, "properties": { "method": "POST", "url": "https://mautic.falahos.my/api/notes/new", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "sendBody": true, "bodyParameters": { "parameters": [ { "name": "text", "value": "={{ '💚 ' + $json.donationLabel + ': ' + $json.amountDisplay + ' via Polar.sh' }}" }, { "name": "type", "value": "donation" } ] }, "options": {} }, "notes": "Adds a timeline note in Mautic recording the donation. The contact ID from the upsert response is used as the parent." }, { "id": "twenty-upsert", "name": "Twenty CRM - Upsert Contact", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [850, 500], "credentials": { "httpRequest": { "id": "SET_ME_TwentyCredId", "name": "Twenty CRM API" } }, "properties": { "method": "POST", "url": "SET_ME_TwentyURL/rest/contacts", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "sendBody": true, "bodyParameters": { "parameters": [ { "name": "email", "value": "={{ $json.email }}" }, { "name": "name", "value": "={{ $json.name }}" }, { "name": "position", "value": "={{ $json.donationLabel + ' Donor' }}" } ] }, "options": {} }, "notes": "SET ME: Configure Twenty CRM API URL and API key. Currently disabled. Activate when Twenty is set up." }, { "id": "twenty-note", "name": "Twenty CRM - Log Donation Note", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [850, 700], "credentials": { "httpRequest": { "id": "SET_ME_TwentyCredId", "name": "Twenty CRM API" } }, "properties": { "method": "POST", "url": "SET_ME_TwentyURL/rest/activities", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "sendBody": true, "bodyParameters": { "parameters": [ { "name": "title", "value": "={{ $json.donationLabel }}" }, { "name": "body", "value": "={{ 'Donation of ' + $json.amountDisplay + ' via Polar.sh' }}" }, { "name": "type", "value": "Note" } ] }, "options": {} }, "notes": "SET ME: Configure when Twenty CRM is set up. Logs donation as an activity note." }, { "id": "done", "name": "Done", "type": "n8n-nodes-base.noOp", "typeVersion": 1, "position": [1050, 300], "properties": {} } ], "connections": { "Polar Webhook": { "main": [ [ { "node": "Parse Polar Payload", "type": "main", "index": 0 } ] ] }, "Parse Polar Payload": { "main": [ [ { "node": "Mautic - Upsert Contact", "type": "main", "index": 0 }, { "node": "Mautic - Add Donation Note", "type": "main", "index": 0 }, { "node": "Twenty CRM - Upsert Contact", "type": "main", "index": 0 }, { "node": "Twenty CRM - Log Donation Note", "type": "main", "index": 0 } ] ] }, "Mautic - Upsert Contact": { "main": [ [ { "node": "Done", "type": "main", "index": 0 } ] ] }, "Mautic - Add Donation Note": { "main": [ [ { "node": "Done", "type": "main", "index": 0 } ] ] }, "Twenty CRM - Upsert Contact": { "main": [ [ { "node": "Done", "type": "main", "index": 0 } ] ] }, "Twenty CRM - Log Donation Note": { "main": [ [ { "node": "Done", "type": "main", "index": 0 } ] ] } }, "settings": { "timezone": "Asia/Kuala_Lumpur" }, "staticData": null, "tags": ["polar", "crm", "donations", "nur-falah"] }