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": "modellogue-slack-relay",
"nodes": [
{
"parameters": {
"trigger": "message",
"channel": "={{ $env.SLACK_RELAY_CHANNEL_ID }}",
"options": {}
},
"type": "n8n-nodes-base.slackTrigger",
"typeVersion": 1,
"position": [
400,
300
],
"id": "9b2a14a0-0001-0001-0001-000000000001",
"name": "Slack Trigger",
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"notes": "Listens for new messages on the channel given by SLACK_RELAY_CHANNEL_ID. Bot Scopes required: channels:history, users:read."
},
{
"parameters": {
"jsCode": "// SR-132 read-direction buffer push.\n// Normalises the Slack event into the relay's RelayedSlackMessage\n// shape and pushes onto a ring buffer of the last 100 messages\n// in workflow-global Static Data.\nconst BUF_KEY = 'slackRelayBuffer';\nconst BUF_MAX = 100;\nconst sd = $getWorkflowStaticData('global');\nif (!Array.isArray(sd[BUF_KEY])) sd[BUF_KEY] = [];\n\n// Slack event payload shape varies by event subtype. We only relay\n// plain messages from real users; bot/edits/deletes are ignored to\n// keep the chat pane uncluttered.\nconst evt = $json;\nconst subtype = evt.subtype || null;\nif (subtype && subtype !== 'thread_broadcast') {\n return [];\n}\nif (!evt.ts || !evt.text || !evt.user) return [];\n\n// Display name resolution: Slack returns a user id (UXXX). The\n// trigger node may include user_profile depending on settings; if\n// not, the relay's polling consumer can fall back to the bare id.\nconst username =\n evt.user_profile?.display_name ||\n evt.user_profile?.real_name ||\n evt.user_profile?.name ||\n evt.user;\n\nconst entry = {\n ts: String(evt.ts),\n user: String(evt.user),\n username: String(username),\n text: String(evt.text),\n};\nsd[BUF_KEY].push(entry);\nif (sd[BUF_KEY].length > BUF_MAX) {\n sd[BUF_KEY] = sd[BUF_KEY].slice(-BUF_MAX);\n}\nreturn [{ json: entry }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
620,
300
],
"id": "9b2a14a0-0001-0001-0001-000000000002",
"name": "Push to Buffer"
},
{
"parameters": {
"httpMethod": "GET",
"path": "slack-relay",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
400,
600
],
"id": "9b2a14a0-0001-0001-0001-000000000003",
"name": "Webhook GET slack-relay",
"notes": "GET /webhook/slack-relay?since=<ts>. ModelLogue's slackRelay service polls this every 1.5s while a session is in REVIEW phase (ADR-017)."
},
{
"parameters": {
"jsCode": "// SR-132 read-direction buffer read.\n// Returns ring-buffer entries with ts strictly greater than ?since.\n// When ?since is omitted or empty, returns the whole buffer (the\n// client treats that as 'first poll, get current snapshot').\nconst BUF_KEY = 'slackRelayBuffer';\nconst sd = $getWorkflowStaticData('global');\nconst buf = Array.isArray(sd[BUF_KEY]) ? sd[BUF_KEY] : [];\n\nconst input = $input.first()?.json || {};\nconst sinceRaw = input.query?.since ?? '';\nconst since = parseFloat(String(sinceRaw));\n\nconst filtered = Number.isFinite(since)\n ? buf.filter((m) => parseFloat(m.ts) > since)\n : buf;\n\nreturn [{ json: { messages: filtered } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
620,
600
],
"id": "9b2a14a0-0001-0001-0001-000000000004",
"name": "Read Buffer"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json.messages }}",
"options": {
"responseHeaders": {
"entries": [
{
"name": "Access-Control-Allow-Origin",
"value": "*"
},
{
"name": "Access-Control-Allow-Methods",
"value": "GET, OPTIONS"
},
{
"name": "Access-Control-Allow-Headers",
"value": "Content-Type"
}
]
}
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
840,
600
],
"id": "9b2a14a0-0001-0001-0001-000000000005",
"name": "Respond GET"
}
],
"connections": {
"Slack Trigger": {
"main": [
[
{
"node": "Push to Buffer",
"type": "main",
"index": 0
}
]
]
},
"Webhook GET slack-relay": {
"main": [
[
{
"node": "Read Buffer",
"type": "main",
"index": 0
}
]
]
},
"Read Buffer": {
"main": [
[
{
"node": "Respond GET",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"tags": [
"modellogue",
"slack-relay",
"ur-017"
]
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
slackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
modellogue-slack-relay. Uses slackTrigger. Event-driven trigger; 5 nodes.
Source: https://github.com/sho1884/ModelLogue/blob/206db72223bccda9c5e31c5e92548c235cd28bf8/n8n/workflows/modellogue-slack-relay.json — 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.
Vendorbot Form Filler. Uses executeCommand, gmail, telegram, slack. Event-driven trigger; 39 nodes.
My Workflow. Uses slackTrigger, httpRequest, slack, googleDrive. Event-driven trigger; 38 nodes.
Type in Slack. Walk away. Get a professional PDF report and a structured Excel fix sheet delivered to Google Drive and posted back in your Slack thread — fully automated, zero manual work.
Create-Issue-Linear-Slack. Uses linear, postgres, slackTrigger, httpRequest. Event-driven trigger; 33 nodes.
This workflow automates the full company enrichment pipeline: Simply import CSV company lists to Slack and save time on enrichment and CRM maintenance. It processes uploaded files, extracts company do