{
  "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
          }
        ]
      ]
    }
  }
}