{
  "name": "Coolie: competitor churn → Slack alert",
  "nodes": [
    {
      "parameters": {
        "content": "## Coolie: alert Slack when competitor churn is detected\n\nWhen a Coolie agent finds a company that stopped using a competitor's product, this workflow finds the contact's email through Coolie's API and alerts a Slack channel.\n\n### How it works\n1. **Coolie webhook** receives the **lead.created** event, raw body kept.\n2. **Verify Coolie signature** checks the `Coolie-Signature` header with your webhook's secret and stops anything else.\n3. **Competitor churn only** keeps the leads whose signal is `competitor-churn`, and Coolie's test event.\n4. **Find the email** calls `POST /api/v1/agents/{agentId}/leads/contact`: 0.1 credit when an email is found, nothing when none is found or when the lead already has one.\n5. **Lead and email** puts the lead and the email together.\n6. **Alert Slack** sends the alert.\n\n### Setup\n1. In **Find the email**, create a **Header Auth** credential: name `Authorization`, value `Bearer` followed by a space and your Coolie API key (Settings → API keys).\n2. In **Alert Slack**, connect Slack and pick the channel.\n3. Copy the **Production URL** of **Coolie webhook**.\n4. In Coolie: **Settings → Webhooks**, paste the URL, tick **New lead**, click **Add webhook**, then **Reveal** the secret.\n5. Paste the secret at the top of **Verify Coolie signature**.\n6. Save and publish the workflow (on n8n before 2.0, activate it).\n7. Click **Send test** in Coolie: a test alert appears (its sample lead has no email to find).\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/competitor-churn-to-slack",
        "height": 820,
        "width": 480
      },
      "id": "cc8c01c6-8049-46a0-8492-c03cdbfba242",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -560,
        -280
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "coolie-churn-alert",
        "responseMode": "onReceived",
        "options": {
          "rawBody": true
        }
      },
      "id": "36e33f0d-edd3-4c9d-8678-8e28453df67c",
      "name": "Coolie webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "c14719c1-d5aa-4644-8753-858d3ee1799b"
    },
    {
      "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": "c665dce6-bfcf-4b95-8d5f-4e588256758a",
      "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": "fb8b78ce-2d39-4f37-845d-262f1ad7e7cc",
              "leftValue": "={{ $json.type }}/{{ $json.data.lead ? $json.data.lead.signal.type : '' }}",
              "rightValue": "lead.created/competitor-churn",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            },
            {
              "id": "3f7ff085-c072-4896-82d1-2f67e242d980",
              "leftValue": "={{ $json.type }}",
              "rightValue": "test",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "or"
        },
        "options": {}
      },
      "id": "96d0af67-8781-4954-8fd6-66be0c0ffd07",
      "name": "Competitor churn only",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        480,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://trycoolie.ai/api/v1/agents/{{ $json.data.lead.agentId || 'none' }}/leads/contact",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ runId: $json.data.lead.runId, prospectId: $json.data.lead.prospectId, field: 'email' }) }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "6399f3b8-df93-458b-808e-4090d4338e77",
      "name": "Find the email",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        720,
        0
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "dfdaed7c-c2ba-46d8-8726-62f5af40534b",
              "name": "lead",
              "value": "={{ $('Competitor churn only').item.json.data.lead }}",
              "type": "object"
            },
            {
              "id": "5ffb8939-3d50-4d34-86af-7b4c4e360fe7",
              "name": "test",
              "value": "={{ $('Competitor churn only').item.json.type === 'test' }}",
              "type": "boolean"
            },
            {
              "id": "af99158f-5626-4512-8394-4097eb19717d",
              "name": "email",
              "value": "={{ $json.found === true ? $json.value : '' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "f0cd74b2-c4d9-48bd-843c-344f636ee322",
      "name": "Lead and email",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        960,
        0
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "text": "=:rotating_light: *Competitor churn{{ $json.test ? ' (test event)' : '' }}: {{ $json.lead.company.name }}*{{ $json.lead.company.domain ? ' · ' + $json.lead.company.domain : '' }}\n{{ $json.lead.signal.headline || $json.lead.signal.name }}{{ $json.lead.signal.postedAt ? ' on ' + $json.lead.signal.postedAt : '' }}\nContact: {{ $json.lead.contact ? $json.lead.contact.name + ($json.lead.contact.title ? ', ' + $json.lead.contact.title : '') : 'none found yet' }}\nEmail: {{ $json.email || 'not found' }}{{ $json.lead.contact && $json.lead.contact.linkedinUrl ? ' · ' + $json.lead.contact.linkedinUrl : '' }}\nAI score: {{ $json.lead.aiScore }}/10",
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "id": "30d0643a-c238-46a5-8932-18fb5a72d0c0",
      "name": "Alert Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        1200,
        0
      ]
    }
  ],
  "connections": {
    "Coolie webhook": {
      "main": [
        [
          {
            "node": "Verify Coolie signature",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify Coolie signature": {
      "main": [
        [
          {
            "node": "Competitor churn only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Competitor churn only": {
      "main": [
        [
          {
            "node": "Find the email",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Find the email": {
      "main": [
        [
          {
            "node": "Lead and email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lead and email": {
      "main": [
        [
          {
            "node": "Alert Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  }
}
