Building an integration with the slack Event API, to get message reaction events into N8N

Hi there,

I’m trying to figure out how I’d make a workflow to respond when messages in a slack channel have a specific reaction added to them, to mark them for processing and including in a newsletter or similar.

Based on what I see (after a very quick look) in the slack events API here, it looks like you can tell slack to send POST requests to something like a webhook endpoint.

Has anyone here implemented this before?

I’m not experienced enough with n8n to know if I can do it with a webhook trigger node, or whether I’d need to create a whole new Slack Event Trigger node instead.

I’m not against doing that, but I haven’t created a custom node before, so it seemed work asking here before I start on anything.

Ta,

Hey @mrchrisadams,

I haven’t had experience with this particular use case. However, taking a quick look at their docs, I am assuming that using the Webhook node will be a simpler solution. The Real-Time API uses web-socket, and currently, n8n doesn’t support that. Hence, using the Events API would be a better approach.

I have not used the event API yet but we use Slack and n8n with slash commands and that works well for us, Looking at the event api there is an HTTP option and a socket mode so I can’t see why it wouldn’t work.

Ah, thanks @harshil1712 - it looks like the webhook might support this.

I can use the Event API to POST in response to the reaction_added event and set the channel like so on an existing slack app:

And from there, it looks like I need to make the Webhook respond with serve with the challenge sent as a parameter by slack, with a response like this example from their docs:

HTTP 200 OK
Content-type: application/json
{"challenge":"3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P"}

From there, once it’s in a workflow we’re good to do all kinds of things, I think

1 Like

That is exactly how I read it, Let me know how you get on as I have a future project that I want to use the events api for :smiley:

1 Like

OK, I think I almost have this working, but I think I’m stuck, and I reckon it’s the kind of thing fresh eyes will see instantly instead of me spending another hour or so fumbling around with a new UI.

I’m still new to n8n, so I’m going to share what I have here.

I’ve given slack an endpoint to send POST requests to, and they look like this when they make it to n8n:

[
  {
    "headers": {
      "host": "my-cool-app.app.n8n.cloud",
      "x-request-id": "8210940278a6a0fc87b4605a9a329f87",
      "x-real-ip": "10.40.4.185",
      "x-forwarded-for": "10.40.4.185",
      "x-forwarded-host": "my-cool-app.app.n8n.cloud",
      "x-forwarded-port": "443",
      "x-forwarded-proto": "https",
      "x-scheme": "https",
      "content-length": "129",
      "user-agent": "Slackbot 1.0 (+https://api.slack.com/robots)",
      "accept": "*/*",
      "accept-encoding": "gzip,deflate",
      "content-type": "application/json",
      "x-slack-signature": "v0=0d684662c7227af9e037559806a4ab66bf5441933687e9a4cc989b5e5250d8c9",
      "x-slack-request-timestamp": "1633684589"
    },
    "params": {},
    "query": {},
    "body": {
      "token": "njbTKVsNP6ewZJ1WbFyZZgBO",
      "challenge": "CSU5fS2WMnX1z6hRiWWisYMmPdI3duz57o4zg4pZIz6sgZrFYrC1",
      "type": "url_verification"
    }
  }
]

I think I need to get the challenge bit out, and return it in a RAW output on the webook

But this is the response I get from slack - it seems this challenge it not being added to the output, as I’m getting this error:

Your URL didn’t respond with the value of the challenge parameter.

Here’s the json slack is saying it gets back

"body": { 
	 "type": "url_verification",
	 "token": "",
	 "challenge": ""
}
Your Response:
"code": 
"error": "challenge_failed"
"body": {
  
}

What am I doing wrong here?

It’s not obvious to me what I’m doing wrong, so I’ve now created a “mock HTTP request” workflow , to trigger the other one for debugging purposes.

The Mock HTTP Request workflow

{
  "name": "Mock A Slack Webhook POST Request",
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        270,
        170
      ]
    },
    {
      "parameters": {
        "requestMethod": "POST",
        "url": "https://productscience.app.n8n.cloud/webhook-test/13305e2a-c6bd-490e-a3cf-20930e84d43a",
        "responseFormat": "=json",
        "jsonParameters": "={{true}}",
        "options": {},
        "bodyParametersJson": "{\n    \"token\": \"Jhj5dZrVaK7ZwHHjRyZWjbDl\",\n    \"challenge\": \"3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P\",\n    \"type\": \"url_verification\"\n}\n"
      },
      "name": "HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        580,
        160
      ]
    }
  ],
  "connections": {
    "Start": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {},
  "id": 1003
}

The Webhook workflow to respond to this

Here’s what the webhook response workflow looks like.

It should just show a response in the UI, and it should be importable into any n8n instance.

I can’t seem to set the response to return a response to slack like so:


{ "challenge": "the-string-sent-by-slack"}

I’ve tried every variant I can think of for setting the response data including messing with the response nodes. Here’s a webhook in its current state.

{
  "name": "Slack reaction webhook",
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        280,
        210
      ],
      "disabled": true
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "13305e2a-c6bd-490e-a3cf-20930e84d43a",
        "responseMode": "lastNode",
        "options": {
          "responseHeaders": {
            "entries": []
          }
        }
      },
      "name": "Example Webhook with response",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        480,
        210
      ],
      "webhookId": "13305e2a-c6bd-490e-a3cf-20930e84d43a"
    }
  ],
  "connections": {},
  "active": false,
  "settings": {},
  "id": 1002
}

I’m sure it’s something really obvious that I’m missing, so I’d appreciate some pointers on what i need to change, as it feels like I’m almost there.

What about just doing this…

{
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "reaction",
        "responseMode": "lastNode",
        "options": {
          "responseHeaders": {
            "entries": []
          }
        }
      },
      "name": "Example Webhook with response",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        10,
        400
      ],
      "webhookId": "13305e2a-c6bd-490e-a3cf-20930e84d43a"
    },
    {
      "parameters": {
        "keepOnlySet": true,
        "values": {
          "string": [
            {
              "name": "challenge",
              "value": "={{$json[\"body\"][\"challenge\"]}}"
            }
          ]
        },
        "options": {}
      },
      "name": "Set",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        210,
        400
      ]
    }
  ],
  "connections": {
    "Example Webhook with response": {
      "main": [
        [
          {
            "node": "Set",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

So we use a Set which only returns the challenge

In the mock test this results in this coming back which might be what you are after.

Hmm… ah, this is almost what we need.

I think slack needs the HTTP Response to look like one of the folllowing, based on Slack’s own API docs:

HTTP 200 OK
Content-type: text/plain
some-long-string

OR form encoded

HTTP 200 OK
Content-type: application/x-www-form-urlencoded
challenge=some-long-string

Or finally, as JSON:

HTTP 200 OK
Content-type: application/json
{ "challenge": "some-long-string" }

Any of these ought to work, but I’m not sure how to control the output to fit these without creating a totally custom node.

How can I do this?

It looks like the example I posted would do the job, Below is a test curl command that posts the same data to my webhook and the response back is what you are after.

n8n does some clever stuff internally so the mock Slack request you made won’t be the actual “raw” response.

curl -X POST https://n8n.mydomain.tld/webhook/whatever-my-path-is -H 'Content-Type: application/json' -d '{"token": "Jhj5dZrVaK7ZwHHjRyZWjbDl", "challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P", "type": "url_verification"}'

Reponse

{"challenge":"3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P"}

Nice! OK, So this does give slack the return value for setting up the endpoint - huzzah!

Now, the next bit is to figure out how to actually pipe the content of the webhooks to something useful!

I figure if I listen for the reaction_added event, and activate the workflow, based on what I read here in the docs, every time a reaction is added to a message, a JSON payload like the one below should be sent:

{
	"token": "z26uFbvR1xHJEdHE1OQiO6t8",
	"team_id": "T061EG9RZ",
	"api_app_id": "A0FFV41KK",
	"event": {
		"type": "reaction_added",
		"user": "U061F1EUR",
		"item": {
			"type": "message",
			"channel": "C061EG9SL",
			"ts": "1464196127.000002"
		},
		"reaction": "slightly_smiling_face",
		"item_user": "U0M4RL1NY",
		"event_ts": "1465244570.336841"
	},
	"type": "event_callback",
	"authed_users": [
		"U061F7AUR"
	],
	"authorizations": [
        {
            "enterprise_id": "E12345",
            "team_id": "T12345",
            "user_id": "U12345",
            "is_bot": false
        }
    ],
	"event_id": "Ev9UQ52YNA",
	"event_context": "EC12345",
	"event_time": 1234567890
}


This is taken from the helpful reaction_added example in the slack API docs, to the end point I’ve specified in n8n cloud.

How can I tell if I’m triggering events?

This seems sensible enough, and it looks like I have the correct permissions set up, but even after making this workflow as active, I’m not seeing any executions in the workflow after the initial one setting the endpoint up.

At this point, I’m now wondering if I DO need to make a whole specialised node to handle the signup process with the challenge as well as as the actual payloads being sent on a regular afterwards.

I can’t see the if the problem is slack or n8n right now, so I’ll likely write a small server I can exposes at a webhook endpoint point to sanity check what I’m doing, and see where I’m getting this wrong.

I guess on the bright side, we pretty much have a tutorial that can be made if we ever get this working…

Do you maybe need an if statement to check if the request is a verify call or a reaction being sent?

Hmm… I think you’re right.

OK. End of the day. I’ll have another look at this after some sleep. Thanks for your help @jon :+1:

1 Like

No problem, I plan to use your work for my own stuff later so it all helps.