n8n and Make template

Coolie + Slack: post every new lead to a channel

This n8n workflow receives Coolie's lead.created webhook, checks its signature, and posts each new lead to a Slack channel: the company, the buying signal, the contact with their email, and the AI score. Import the JSON file into n8n, connect Slack, add the workflow's address in Coolie under Settings → Webhooks and paste the webhook's secret. In Make, the same flow is a custom webhook, a filter and a Slack module.

Updated · Works with n8n, Make, Slack · Coolie event: lead.created

What the workflow does

  1. Coolie webhook (Webhook)

    Receives Coolie's POST, answers at once, and keeps the raw body, which the signature is computed on.

  2. Verify Coolie signature (Code)

    Recomputes the Coolie-Signature header, an HMAC-SHA256 of the timestamp, a dot and the raw body with your webhook's secret, compares it in constant time, refuses a request older than five minutes, and passes the event on.

  3. New leads only (IF)

    Lets lead.created events through, and Coolie's test event while you set it up.

  4. Post to Slack (Slack)

    Posts the company and its domain, the signal and its date, the contact and their email, the AI score and the signal's link.

Set it up in n8n

  1. Import the workflow

    Download the JSON file below. In n8n, open a new workflow and choose Import from File, or Import from URL with https://trycoolie.ai/templates/n8n/coolie-new-lead-to-slack.json, or paste the JSON onto the canvas.

  2. Connect Slack

    Open Post to Slack, create or pick a Slack credential, and choose the channel.

  3. Copy the webhook's address

    Open Coolie webhook and copy its Production URL: the address Coolie will post to.

  4. Add the webhook in Coolie

    In Coolie, open Settings → Webhooks, paste the address, tick New lead (lead.created) and click Add webhook: nothing is sent yet. Then click Reveal and copy the webhook's secret, which starts with whsec_.

  5. Paste the secret

    In n8n, open Verify Coolie signature and paste the secret between the quotes on its first lines. Without it, the node stops every event, with an error that says so.

  6. Publish the workflow

    Save, then publish the workflow (on n8n before version 2.0, switch it to active): its Production URL now answers.

  7. Send a test

    Back in Coolie, click Send test on the webhook: a sample lead, marked as a test event, appears in your channel.

coolie-new-lead-to-slack.json

An n8n workflow (Webhook, Code, IF and the tools' own nodes), with a note on the canvas that repeats these steps.

Download the JSON file

Show the workflow JSON
coolie-new-lead-to-slack.json
{
  "name": "Coolie: new lead → Slack",
  "nodes": [
    {
      "parameters": {
        "content": "## Coolie: post every new lead to Slack\n\nEach lead a Coolie agent finds arrives here as a **lead.created** webhook and is posted to a Slack channel: the company, the buying signal, the contact and their email, the AI score.\n\n### How it works\n1. **Coolie webhook** receives the event, raw body kept.\n2. **Verify Coolie signature** checks the `Coolie-Signature` header with your webhook's secret and stops anything else.\n3. **New leads only** lets `lead.created` events through, and Coolie's test event.\n4. **Post to Slack** sends the message.\n\n### Setup\n1. In **Post to Slack**, connect Slack and pick the channel.\n2. Copy the **Production URL** of **Coolie webhook**.\n3. In Coolie: **Settings → Webhooks**, paste the URL, tick **New lead**, click **Add webhook**, then **Reveal** the secret.\n4. Paste the secret at the top of **Verify Coolie signature**.\n5. Save and publish the workflow (on n8n before 2.0, activate it).\n6. Click **Send test** in Coolie: a sample lead appears in Slack.\n\nSelf-hosted n8n: allow the crypto module (`NODE_FUNCTION_ALLOW_BUILTIN=crypto`, on the task runners when you use them).\nGuide: https://trycoolie.ai/developers/templates/new-lead-to-slack",
        "height": 680,
        "width": 480
      },
      "id": "43277fe8-f0e4-4463-8be6-98831b532423",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -560,
        -280
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "coolie-new-lead-slack",
        "responseMode": "onReceived",
        "options": {
          "rawBody": true
        }
      },
      "id": "def7181d-f424-4875-82d9-88170030a0c1",
      "name": "Coolie webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "8b74fdd9-09a6-440c-8af8-f78ffec1563c"
    },
    {
      "parameters": {
        "jsCode": "// Coolie signs every event: Coolie-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of \"t.body\">.\n// This node recomputes it on the raw body with your webhook's secret, and stops anything else.\n// Paste the secret below: Coolie > Settings > Webhooks > Reveal (it starts with whsec_).\nconst SECRET = 'PASTE_YOUR_COOLIE_WEBHOOK_SECRET';\n\nconst crypto = require('crypto');\nif (!SECRET.startsWith('whsec_')) {\n  throw new Error('Paste your Coolie webhook secret (whsec_...) in the Verify Coolie signature node.');\n}\nconst request = $input.first().json;\nconst rawBody = (await this.helpers.getBinaryDataBuffer(0, 'data')).toString('utf8');\nconst parts = Object.fromEntries(\n  String(request.headers['coolie-signature'] || '').split(',').map((part) => part.split('=')),\n);\nconst expected = Buffer.from(crypto.createHmac('sha256', SECRET).update(parts.t + '.' + rawBody).digest('hex'));\nconst given = Buffer.from(String(parts.v1 || ''));\nconst fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;\nif (!fresh || given.length !== expected.length || !crypto.timingSafeEqual(given, expected)) {\n  throw new Error('Not a Coolie event: the signature does not match.');\n}\nreturn [{ json: JSON.parse(rawBody), pairedItem: { item: 0 } }];"
      },
      "id": "f27ed160-209c-48d1-8bbe-ed712357b9b4",
      "name": "Verify Coolie signature",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "65702f04-93d5-4a7b-8494-7ab25aa2756d",
              "leftValue": "={{ $json.type }}",
              "rightValue": "lead.created",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            },
            {
              "id": "0bcf2c7f-2a19-4703-8616-112d740929ff",
              "leftValue": "={{ $json.type }}",
              "rightValue": "test",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "or"
        },
        "options": {}
      },
      "id": "3af36dda-7d95-4ca6-8b62-2a5aa6d8db9a",
      "name": "New leads only",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        480,
        0
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "text": "=:sparkles: *New lead{{ $json.type === 'test' ? ' (test event)' : '' }}: {{ $json.data.lead.company.name }}*{{ $json.data.lead.company.domain ? ' · ' + $json.data.lead.company.domain : '' }}\n*{{ $json.data.lead.signal.name }}*{{ $json.data.lead.signal.headline ? ': ' + $json.data.lead.signal.headline : '' }}{{ $json.data.lead.signal.postedAt ? ' (' + $json.data.lead.signal.postedAt + ')' : '' }}\nContact: {{ $json.data.lead.contact ? $json.data.lead.contact.name + ($json.data.lead.contact.title ? ', ' + $json.data.lead.contact.title : '') : 'none found yet' }}\nEmail: {{ $json.data.lead.contact && $json.data.lead.contact.email ? $json.data.lead.contact.email : 'not found yet' }}\nAI score: {{ $json.data.lead.aiScore }}/10{{ $json.data.lead.signal.url ? ' · ' + $json.data.lead.signal.url : '' }}",
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "id": "62b1f7a6-1955-481d-88f3-f47a5a968642",
      "name": "Post to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        720,
        0
      ]
    }
  ],
  "connections": {
    "Coolie webhook": {
      "main": [
        [
          {
            "node": "Verify Coolie signature",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify Coolie signature": {
      "main": [
        [
          {
            "node": "New leads only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "New leads only": {
      "main": [
        [
          {
            "node": "Post to Slack",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  }
}

Build it in Make

Make's scenario files are its own, so here is the same flow, module by module, in a new scenario.

  1. Create the webhook in Make

    In a new scenario, add Webhooks › Custom webhook, click Add, name it (Coolie leads) and copy its address. Make then waits for a first request to learn the data's structure.

  2. Send Coolie's test event

    In Coolie, open Settings → Webhooks, paste the address, tick New lead (lead.created), click Add webhook, then Send test. Make shows that it determined the structure from the test event, which carries a sample lead in the real shape.

  3. Add the Slack message

    Add Slack › Create a Message, connect Slack, choose the channel, and write the text from the webhook's fields: data › lead › company › name, signal › name and headline, contact › name and email, aiScore.

  4. Filter on new leads

    On the link between the two modules, set up a filter: type Equal to lead.created. While you try it, add an OR condition, type Equal to test.

  5. Turn the scenario on

    Save, and switch the scenario on, set to run immediately as data arrives.

  6. Optional: check Coolie's signature

    A Make webhook address is long and random: keep it private. To refuse anything Coolie did not sign, turn on Get request headers and JSON pass-through in the webhook's advanced settings, parse the text it passes with JSON › Parse JSON, and add a filter comparing the v1 part of the Coolie-Signature header with Make's sha256 function given your secret as its key (it then computes an HMAC, in hex), over the t part of the header, a dot and the raw body.

The event your workflow receives

A lead.created event, as Coolie posts it. Coolie's test event has the same shape, with type test and a sample lead. To map fields in Make before the first event, paste this into its data structure generator. The values are examples.

JSON
{
  "id": "evt_Tq4mZ8cW2xLp7VbN",
  "type": "lead.created",
  "createdAt": "2026-10-19T09:02:14.020Z",
  "data": {
    "lead": {
      "runId": "ui-5b9e2c41",
      "prospectId": "p-41b7c2d9e0aa",
      "agentId": "agent-3f2a91c0",
      "status": "enriched",
      "processedAt": "2026-10-19T09:02:13.511Z",
      "icpMatch": true,
      "aiScore": 8.4,
      "feedback": null,
      "signal": {
        "type": "hiring",
        "name": "Hiring",
        "headline": "Operations Manager",
        "detail": null,
        "url": "https://jobs.example.com/12345",
        "postedAt": "2026-10-17"
      },
      "company": {
        "name": "Northwind Health",
        "domain": "northwind.example",
        "website": "https://northwind.example",
        "linkedinUrl": "https://www.linkedin.com/company/example",
        "industry": "Hospitals and Health Care",
        "employees": 320,
        "country": "France",
        "locations": [
          "Lyon, France"
        ]
      },
      "contact": {
        "name": "Camille Martin",
        "title": "COO",
        "seniority": "c_suite",
        "linkedinUrl": "https://www.linkedin.com/in/example",
        "country": "France",
        "email": "camille.martin@northwind.example",
        "emailConfidence": 0.92,
        "phone": null
      },
      "research": null,
      "sequence": null
    }
  }
}

Frequently asked questions

Does Coolie have a Slack app?

No. This template connects the two through Coolie's signed webhooks: Coolie posts each event to n8n or Make, which posts to Slack. The same events can feed any tool n8n or Make reaches.

Why check the signature in n8n?

Anyone who finds the webhook's address could post fake leads to your channel. Verify Coolie signature checks each request against your webhook's secret and stops anything else.

Can I post only some leads?

Yes: add a condition to New leads only, for example on data.lead.aiScore or on data.lead.signal.type, or start from the competitor churn alert.

My self-hosted n8n refuses require('crypto'). What do I do?

The Code node that checks the signature needs Node's crypto module. n8n Cloud allows it; on a self-hosted n8n, set NODE_FUNCTION_ALLOW_BUILTIN=crypto (on the task runners when you use them) and restart.

What does it cost on Coolie's side?

Nothing more: webhooks are part of every plan, and the leads are those your agents find. n8n, Make and Slack bill as they usually do.

How the events are signed and retried: receive Coolie webhooks and verify their signature. Every template: Coolie workflow templates.

Your next conversation is already happening somewhere.

Set up an agent in five minutes. The first leads arrive with their reason to write.