This workflow corresponds to n8n.io template #17989 — we link there as the canonical source.
This workflow follows the Gmail → Slack 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": "Create Odoo CRM leads from a webhook and auto-follow up after 3 days",
"tags": [],
"nodes": [
{
"id": "cd884bf5-c53e-4522-b627-d2b2c7093003",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-864,
16
],
"parameters": {
"width": 480,
"height": 896,
"content": "## Create Odoo CRM leads from a webhook and auto-follow up after 3 days\n\n### How it works\n\nThis workflow receives new lead submissions via a webhook, validates the required input, creates a CRM lead in Odoo, and sends immediate notifications. It replies to the lead by email, notifies the sales team in Slack, and returns a success response to the webhook caller. In parallel, it waits 3 days, checks whether the Odoo lead is still in the initial stage, and sends a follow-up reminder if no progress has been made.\n\n### Setup steps\n\n- Configure the webhook endpoint and ensure incoming payloads include the lead fields expected by the validation and Odoo create steps.\n- Add and test Odoo credentials, including access to the CRM lead model and the fields used to create and re-fetch leads.\n- Connect Gmail credentials for both the immediate auto-reply and the delayed follow-up reminder emails.\n- Connect Slack credentials and set the target sales channel for new-lead notifications.\n- Ensure n8n wait/resume execution settings are enabled and reliable for the 3-day delay, especially if using queue mode or external storage.\n- Review the invalid-input and Odoo-failure webhook responses so they return the correct status codes and error messages for your integration.\n\n### Customization\n\nAdjust the validation rules, Odoo lead field mapping, email templates, Slack channel/message, 3-day wait duration, and the Odoo stage condition that determines whether a follow-up reminder should be sent."
},
"typeVersion": 1
},
{
"id": "816538d3-49fd-47d4-9465-ef7b7ecf557b",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-304,
272
],
"parameters": {
"color": 7,
"width": 432,
"height": 592,
"content": "## Receive and validate lead\n\nHandles the incoming webhook, checks whether the lead payload is valid, and returns a 400 response for invalid submissions."
},
"typeVersion": 1
},
{
"id": "baaa3b68-5527-4569-9240-1b2597048825",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
176,
224
],
"parameters": {
"color": 7,
"width": 240,
"height": 640,
"content": "## Create Odoo lead\n\nCreates the CRM lead in Odoo after validation and returns a 502 webhook response if the Odoo create operation fails."
},
"typeVersion": 1
},
{
"id": "e0e3e0a5-aac4-4ea1-a198-fae8d70b5883",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
272
],
"parameters": {
"color": 7,
"width": 432,
"height": 320,
"content": "## Send lead auto-reply\n\nSends an immediate Gmail auto-reply to the new lead and uses a pass-through combine node to continue the successful path."
},
"typeVersion": 1
},
{
"id": "ab998b31-567e-4b69-8434-c0adc3d631c5",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1008,
256
],
"parameters": {
"color": 7,
"width": 368,
"height": 336,
"content": "## Notify sales team\n\nPosts the new lead notification to Slack and then combines the Slack path before branching into the immediate response and delayed follow-up flow."
},
"typeVersion": 1
},
{
"id": "aeb3acf1-7b41-49be-b97a-f70e484cf7de",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
1408,
16
],
"parameters": {
"color": 7,
"width": 240,
"height": 368,
"content": "## Return success response\n\nSends the webhook success response once the lead has been created and the initial notifications have been processed."
},
"typeVersion": 1
},
{
"id": "2c7874b0-454c-4fd0-a1b7-17018faadb3a",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
1408,
528
],
"parameters": {
"color": 7,
"width": 432,
"height": 624,
"content": "## Delay and reload stage\n\nWaits 3 days, re-fetches the lead stage from Odoo, and includes the nearby background failure sink used when the delayed task or follow-up branch needs to be logged for review."
},
"typeVersion": 1
},
{
"id": "eadb0730-da43-43ad-a319-2b60690ff5f1",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
1872,
448
],
"parameters": {
"color": 7,
"width": 432,
"height": 544,
"content": "## Decide follow-up action\n\nChecks whether the lead is still in the new stage, sends a Gmail follow-up reminder when needed, or ends the delayed branch if the lead has already progressed."
},
"typeVersion": 1
},
{
"id": "88554f9d-4385-4f61-8179-adff064f1e17",
"name": "When New Lead Posted",
"type": "n8n-nodes-base.webhook",
"position": [
-256,
432
],
"parameters": {
"path": "odoo-demo/new-lead",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "dfbb74d2-62e5-420f-a97f-d9450fa7610a",
"name": "Check Lead Name Presence",
"type": "n8n-nodes-base.if",
"notes": "Gate: requires a non-empty name AND an email matching a basic email regex. False branch returns 400 without touching Odoo.",
"position": [
-16,
432
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-name-notempty",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.body.name }}",
"rightValue": ""
},
{
"id": "cond-email-valid",
"operator": {
"type": "string",
"operation": "regex"
},
"leftValue": "={{ $json.body.email }}",
"rightValue": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "c918cee2-6f7a-4b43-acd9-c56c7fdbec05",
"name": "Respond Invalid Input",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-16,
704
],
"parameters": {
"options": {
"responseCode": 400
},
"respondWith": "json",
"responseBody": "{\n \"error\": \"validation_error\",\n \"message\": \"A non-empty name and a valid email address are required.\"\n}"
},
"typeVersion": 1.5
},
{
"id": "f890f293-05a8-4978-936f-9568721c77d8",
"name": "Create Odoo CRM Lead",
"type": "n8n-nodes-base.odoo",
"notes": "Creates a crm.lead record (type=lead) in Odoo from the validated webhook payload. Self-heals transient failures (retryOnFail); persistent failures route to the 502 error output.",
"onError": "continueErrorOutput",
"maxTries": 3,
"position": [
224,
432
],
"parameters": {
"resource": "custom",
"operation": "create",
"fieldsToSend": {
"value": {
"name": "={{ $('When New Lead Posted').item.json.body.company ? $('When New Lead Posted').item.json.body.company + ' - ' + $('When New Lead Posted').item.json.body.name : $('When New Lead Posted').item.json.body.name }}",
"type": "lead",
"phone": "={{ $('When New Lead Posted').item.json.body.phone }}",
"email_from": "={{ $('When New Lead Posted').item.json.body.email }}",
"description": "={{ $('When New Lead Posted').item.json.body.message }}",
"contact_name": "={{ $('When New Lead Posted').item.json.body.name }}",
"partner_name": "={{ $('When New Lead Posted').item.json.body.company }}"
},
"schema": [],
"mappingMode": "defineBelow",
"matchingColumns": []
},
"authentication": "odooApi",
"customResource": {
"__rl": true,
"mode": "id",
"value": "crm.lead"
}
},
"retryOnFail": true,
"typeVersion": 2,
"waitBetweenTries": 5000
},
{
"id": "ba0f8ca2-d7ed-491d-b014-4aac5daa6637",
"name": "Respond Odoo Creation Failed",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
224,
704
],
"parameters": {
"options": {
"responseCode": 502
},
"respondWith": "json",
"responseBody": "={{ { \"error\": \"upstream_error\", \"message\": \"Failed to create the CRM lead in Odoo: \" + ($json.error && $json.error.message ? $json.error.message : ($json.message || 'unknown error')) } }}"
},
"typeVersion": 1.5
},
{
"id": "da408877-4775-41e4-a68d-0e75caa0cf62",
"name": "Send Gmail Auto-Reply",
"type": "n8n-nodes-base.gmail",
"notes": "Auto-reply to the lead. Wired with continueErrorOutput so a bad/undeliverable address never crashes the pipeline \u2014 the CRM lead is already created either way.",
"onError": "continueErrorOutput",
"maxTries": 3,
"position": [
592,
432
],
"parameters": {
"sendTo": "={{ $('When New Lead Posted').item.json.body.email }}",
"message": "={{ '<p>Hi ' + $('When New Lead Posted').item.json.body.name + ',</p><p>Thanks for contacting us' + ($('When New Lead Posted').item.json.body.company ? ' on behalf of ' + $('When New Lead Posted').item.json.body.company : '') + '. We have logged your request (Lead #' + $json.id + ') and a member of our sales team will get back to you within one business day.</p><p>For reference, here is what we received:</p><blockquote>' + $('When New Lead Posted').item.json.body.message + '</blockquote><p>Best regards,<br>The Sales Team</p>' }}",
"options": {},
"subject": "={{ 'Thanks for reaching out, ' + $('When New Lead Posted').item.json.body.name + '!' }}"
},
"retryOnFail": true,
"typeVersion": 2.2,
"waitBetweenTries": 5000
},
{
"id": "6f7f6e7f-19b6-4c7d-b2fe-b0f79bd26610",
"name": "Continue After Auto-Reply",
"type": "n8n-nodes-base.noOp",
"notes": "Fan-in anchor: both the auto-reply success path and its error path land here so downstream nodes reference this node by name instead of ambiguous $json.",
"position": [
832,
432
],
"parameters": {},
"typeVersion": 1
},
{
"id": "e899e96d-9945-457b-8103-7751f75a13e6",
"name": "Post to Slack Sales Channel",
"type": "n8n-nodes-base.slack",
"notes": "Credentials intentionally left unset \u2014 pick a Slack credential + channel in the UI. The localhost link is for this demo VPS; swap for the real reachable Odoo host in production. Wired with continueErrorOutput so a not-yet-configured Slack credential can't block lead creation or the success response.",
"onError": "continueErrorOutput",
"maxTries": 3,
"position": [
1056,
432
],
"parameters": {
"text": "=:new: *New CRM Lead Created*\n\n*Name:* {{ $('When New Lead Posted').item.json.body.name }}\n*Company:* {{ $('When New Lead Posted').item.json.body.company || 'n/a' }}\n*Email:* {{ $('When New Lead Posted').item.json.body.email }}\n*Phone:* {{ $('When New Lead Posted').item.json.body.phone || 'n/a' }}\n*Message:* {{ $('When New Lead Posted').item.json.body.message }}\n\n<http://localhost:8069/odoo/crm/{{ $('Create Odoo CRM Lead').item.json.id }}|Open Lead #{{ $('Create Odoo CRM Lead').item.json.id }} in Odoo>",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": ""
},
"otherOptions": {}
},
"retryOnFail": true,
"typeVersion": 2.5,
"waitBetweenTries": 5000
},
{
"id": "8485ee4a-de88-4b3e-bdf7-a71504d335bf",
"name": "Continue Post-Slack Notification",
"type": "n8n-nodes-base.noOp",
"notes": "Fan-in anchor: both the Slack-post success path and its error path land here before the workflow responds and kicks off the follow-up branch.",
"position": [
1232,
432
],
"parameters": {},
"typeVersion": 1
},
{
"id": "5dda212b-e52a-4cc7-bbdd-f9d299bfa72d",
"name": "Confirm Lead Created",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1456,
224
],
"parameters": {
"options": {
"responseCode": 200
},
"respondWith": "json",
"responseBody": "={{ { \"status\": \"success\", \"message\": \"Lead created and sales team notified.\", \"lead_id\": $('Create Odoo CRM Lead').item.json.id } }}"
},
"typeVersion": 1.5
},
{
"id": "53703575-68bc-45f8-b13a-974ee14afdeb",
"name": "Wait 3 Days for Follow-Up",
"type": "n8n-nodes-base.wait",
"notes": "Runs after the webhook has already responded (this branch is parallel to the success response). Models a real 'wait a few days and check back' sales cadence.",
"position": [
1456,
704
],
"parameters": {
"unit": "days",
"amount": 3
},
"typeVersion": 1.1
},
{
"id": "8d018b6f-4805-45ed-9859-2bcd8a3c4995",
"name": "Fetch Latest Lead Stage",
"type": "n8n-nodes-base.odoo",
"notes": "Re-reads the SAME lead created in Stage 2 by id, 3 days later, to inspect its current stage_id.",
"onError": "continueErrorOutput",
"maxTries": 3,
"position": [
1696,
704
],
"parameters": {
"options": {
"fieldsList": "id,name,stage_id,user_id,email_from"
},
"recordId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Create Odoo CRM Lead').item.json.id }}"
},
"resource": "custom",
"operation": "get",
"authentication": "odooApi",
"customResource": {
"__rl": true,
"mode": "id",
"value": "crm.lead"
}
},
"retryOnFail": true,
"typeVersion": 2,
"waitBetweenTries": 5000
},
{
"id": "dff430d5-c46e-4280-a0f4-fb294171dcb3",
"name": "Log Background Task Failure",
"type": "n8n-nodes-base.noOp",
"notes": "Demo stand-in for a real alert (email/Slack on a DIFFERENT channel than the one above, per the recursion-trap rule) when the background follow-up check itself fails.",
"position": [
1696,
992
],
"parameters": {},
"typeVersion": 1
},
{
"id": "5a6c278f-eca8-4bad-8091-5c534bac6aa7",
"name": "Check If Stage Is New",
"type": "n8n-nodes-base.if",
"notes": "True = the lead never left the initial 'New' qualification stage after 3 days \u2192 needs a nudge. False = it progressed \u2192 no action.",
"position": [
1920,
704
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "stage-check",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.stage_id ? $json.stage_id[1] : '' }}",
"rightValue": "New"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "092112df-37dd-4be6-a8e6-eca92872f31e",
"name": "Send Follow-Up Email",
"type": "n8n-nodes-base.gmail",
"notes": "sendTo is a placeholder \u2014 wire this to the real assigned-salesperson lookup (e.g. resolve $json.user_id via res.users) before going live.",
"onError": "continueErrorOutput",
"maxTries": 3,
"position": [
2160,
624
],
"parameters": {
"sendTo": "user@example.com",
"message": "={{ '<p>Hi,</p><p>The CRM lead <strong>#' + $json.id + ' \u2013 ' + $json.name + '</strong> has been sitting in stage <strong>' + ($json.stage_id ? $json.stage_id[1] : 'New') + '</strong> for 3 days with no progress.</p><p>Assigned salesperson: ' + ($json.user_id ? $json.user_id[1] : 'Unassigned') + '</p><p>Please follow up with the lead or update its stage in Odoo.</p>' }}",
"options": {},
"subject": "={{ 'Reminder: Lead #' + $json.id + ' still needs follow-up' }}"
},
"retryOnFail": true,
"typeVersion": 2.2,
"waitBetweenTries": 5000
},
{
"id": "94fcb49b-1d0f-43ab-9edf-446b1210adbf",
"name": "Lead Progressed Notification",
"type": "n8n-nodes-base.noOp",
"notes": "No-op: lead moved past the initial stage on its own, nothing to do.",
"position": [
2160,
832
],
"parameters": {},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1",
"saveManualExecutions": true,
"saveExecutionProgress": true,
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all"
},
"nodeGroups": [],
"connections": {
"Create Odoo CRM Lead": {
"main": [
[
{
"node": "Send Gmail Auto-Reply",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond Odoo Creation Failed",
"type": "main",
"index": 0
}
]
]
},
"Send Follow-Up Email": {
"main": [
[],
[
{
"node": "Log Background Task Failure",
"type": "main",
"index": 0
}
]
]
},
"When New Lead Posted": {
"main": [
[
{
"node": "Check Lead Name Presence",
"type": "main",
"index": 0
}
]
]
},
"Check If Stage Is New": {
"main": [
[
{
"node": "Send Follow-Up Email",
"type": "main",
"index": 0
}
],
[
{
"node": "Lead Progressed Notification",
"type": "main",
"index": 0
}
]
]
},
"Send Gmail Auto-Reply": {
"main": [
[
{
"node": "Continue After Auto-Reply",
"type": "main",
"index": 0
}
],
[
{
"node": "Continue After Auto-Reply",
"type": "main",
"index": 0
}
]
]
},
"Fetch Latest Lead Stage": {
"main": [
[
{
"node": "Check If Stage Is New",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Background Task Failure",
"type": "main",
"index": 0
}
]
]
},
"Check Lead Name Presence": {
"main": [
[
{
"node": "Create Odoo CRM Lead",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond Invalid Input",
"type": "main",
"index": 0
}
]
]
},
"Continue After Auto-Reply": {
"main": [
[
{
"node": "Post to Slack Sales Channel",
"type": "main",
"index": 0
}
]
]
},
"Wait 3 Days for Follow-Up": {
"main": [
[
{
"node": "Fetch Latest Lead Stage",
"type": "main",
"index": 0
}
]
]
},
"Post to Slack Sales Channel": {
"main": [
[
{
"node": "Continue Post-Slack Notification",
"type": "main",
"index": 0
}
],
[
{
"node": "Continue Post-Slack Notification",
"type": "main",
"index": 0
}
]
]
},
"Continue Post-Slack Notification": {
"main": [
[
{
"node": "Confirm Lead Created",
"type": "main",
"index": 0
},
{
"node": "Wait 3 Days for Follow-Up",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow accepts website lead submissions via a webhook, creates a new lead in Odoo CRM, sends an automatic thank-you email through Gmail, and notifies a Slack sales channel, then waits 3 days and emails a follow-up reminder if the lead is still "New". Receives a POST…
Source: https://n8n.io/workflows/17989/ — 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.
Automatically qualify, score, and route inbound B2B leads using GPT-4o-mini — no manual review needed.
This workflow receives new lead form submissions via webhook, uses Groq (OpenAI-compatible Chat Completions) to score and draft a reply, then sends the email via Gmail, logs the lead to Google Sheets
Find companies similar to your best clients using PredictLeads, enrich each with news, hiring, and tech signals, then score them 0–100 for outreach priority.
This workflow monitors Meta Ads and Google Ads campaigns on a daily schedule to detect performance drops. It fetches yesterday’s campaign data, standardizes metrics, and calculates CTR and ROAS agains
This n8n template automates finding roofing contractors in any city using Google Maps. It deep-scrapes listings via ScrapeOps Proxy, deduplicates results against Google Sheets, saves fresh leads, and