Skip to content

Slack Integration

Receive webhooks from Slack for events, slash commands, and interactive components.

Setup

1. Create a Source in WebhookRelay

bash
curl -X POST https://api.webhookrelay.com/api/organizations/{orgId}/sources \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Slack App",
    "slug": "slack",
    "verificationConfig": {
      "type": "slack",
      "secret": "your-signing-secret"
    }
  }'

Save your webhook URL:

https://api.webhookrelay.com/ingest/{orgSlug}/slack

2. Create a Slack App

  1. Go to Slack API
  2. Click Create New App
  3. Choose From scratch
  4. Name your app and select a workspace
  5. Click Create App

3. Configure Event Subscriptions

  1. In your Slack app settings, go to Event Subscriptions
  2. Toggle Enable Events to On
  3. Enter your WebhookRelay URL as the Request URL
  4. Slack will send a verification challenge (WebhookRelay handles this)
  5. Under Subscribe to bot events, add events like:
    • message.channels
    • message.im
    • app_mention
    • reaction_added
  6. Click Save Changes

4. Get Your Signing Secret

  1. Go to Basic Information in your Slack app
  2. Under App Credentials, find Signing Secret
  3. Copy this secret to your WebhookRelay source configuration

5. Install the App

  1. Go to Install App in your Slack app settings
  2. Click Install to Workspace
  3. Authorize the requested permissions

Signature Verification

Slack signs requests using HMAC-SHA256. The signature is sent in headers:

X-Slack-Request-Timestamp: 1531420618
X-Slack-Signature: v0=a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503

WebhookRelay verifies this automatically:

json
{
  "verificationConfig": {
    "type": "slack",
    "secret": "your-signing-secret"
  }
}

Timestamp Tolerance

Slack signatures include a timestamp. WebhookRelay rejects requests older than 5 minutes to prevent replay attacks.

Event Types

Message Events

json
{
  "token": "...",
  "team_id": "T1234567",
  "event": {
    "type": "message",
    "channel": "C1234567",
    "user": "U1234567",
    "text": "Hello world",
    "ts": "1234567890.123456"
  },
  "type": "event_callback",
  "event_id": "Ev1234567",
  "event_time": 1234567890
}

App Mention

json
{
  "token": "...",
  "team_id": "T1234567",
  "event": {
    "type": "app_mention",
    "user": "U1234567",
    "text": "<@U0LAN0Z89> help",
    "channel": "C1234567",
    "ts": "1234567890.123456"
  },
  "type": "event_callback"
}

Reaction Added

json
{
  "token": "...",
  "team_id": "T1234567",
  "event": {
    "type": "reaction_added",
    "user": "U1234567",
    "reaction": "thumbsup",
    "item": {
      "type": "message",
      "channel": "C1234567",
      "ts": "1234567890.123456"
    }
  },
  "type": "event_callback"
}

Slash Commands

Setup

  1. Go to Slash Commands in your Slack app
  2. Click Create New Command
  3. Enter your command (e.g., /mycommand)
  4. Set Request URL to your WebhookRelay URL
  5. Click Save

Payload Format

json
{
  "command": "/mycommand",
  "text": "argument text",
  "user_id": "U1234567",
  "user_name": "username",
  "channel_id": "C1234567",
  "channel_name": "general",
  "team_id": "T1234567",
  "response_url": "https://hooks.slack.com/commands/..."
}

Responding to Commands

Your destination should respond within 3 seconds with:

json
{
  "response_type": "in_channel",
  "text": "Command received!"
}

Or use the response_url for delayed responses.

Interactive Components

Setup

  1. Go to Interactivity & Shortcuts
  2. Toggle Interactivity to On
  3. Set Request URL to your WebhookRelay URL
  4. Click Save Changes

Button Click Payload

json
{
  "type": "block_actions",
  "user": {
    "id": "U1234567",
    "username": "user"
  },
  "actions": [{
    "action_id": "button_click",
    "block_id": "block_1",
    "type": "button",
    "value": "click_me_123"
  }],
  "response_url": "https://hooks.slack.com/actions/...",
  "trigger_id": "1234567890.123456"
}

Transform Examples

Log to Database

javascript
function transform(payload) {
  if (payload.type !== 'event_callback') {
    return payload;
  }

  const event = payload.event;

  return {
    event_type: event.type,
    team_id: payload.team_id,
    channel_id: event.channel,
    user_id: event.user,
    text: event.text,
    timestamp: event.ts,
    raw_event: payload
  };
}

Forward to Discord

javascript
function transform(payload) {
  if (payload.type !== 'event_callback' || payload.event.type !== 'message') {
    return null; // Skip non-message events
  }

  const event = payload.event;

  return {
    content: event.text,
    username: `Slack (${event.user})`,
    embeds: [{
      color: 0x4A154B,
      footer: {
        text: `From Slack #${event.channel}`
      },
      timestamp: new Date(parseFloat(event.ts) * 1000).toISOString()
    }]
  };
}

Extract Mentions

javascript
function transform(payload) {
  if (payload.event?.type !== 'app_mention') {
    return payload;
  }

  const event = payload.event;
  const text = event.text.replace(/<@[A-Z0-9]+>\s*/g, '').trim();

  return {
    action: 'mention',
    user: event.user,
    channel: event.channel,
    command: text,
    timestamp: event.ts
  };
}

Filter Examples

Only Message Events

json
{
  "name": "Messages Only",
  "logic": "and",
  "conditions": [
    {
      "field": "event.type",
      "operator": "equals",
      "value": "message"
    }
  ]
}

Exclude Bot Messages

json
{
  "name": "Non-Bot Messages",
  "logic": "and",
  "conditions": [
    {
      "field": "event.bot_id",
      "operator": "not_exists",
      "value": ""
    }
  ]
}

Specific Channel

json
{
  "name": "Alerts Channel",
  "logic": "and",
  "conditions": [
    {
      "field": "event.channel",
      "operator": "equals",
      "value": "C1234567"
    }
  ]
}

App Mentions Only

json
{
  "name": "App Mentions",
  "logic": "and",
  "conditions": [
    {
      "field": "event.type",
      "operator": "equals",
      "value": "app_mention"
    }
  ]
}

URL Verification

When you first set up Event Subscriptions, Slack sends a verification challenge:

json
{
  "type": "url_verification",
  "challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
  "token": "..."
}

WebhookRelay automatically responds with the challenge, so you don't need to handle this.

Best Practices

  1. Respond quickly: Slack expects responses within 3 seconds for slash commands and interactions

  2. Use response_url: For longer operations, acknowledge immediately and use response_url for the actual response

  3. Handle retries: Slack may retry failed requests; check for X-Slack-Retry-Num header

  4. Filter bot messages: Exclude bot messages to prevent loops

  5. Verify the signature: Always use signature verification in production

Troubleshooting

URL Verification Failed

  1. Ensure your source is configured with the correct signing secret
  2. Check that the WebhookRelay endpoint is accessible from the internet
  3. Verify the URL doesn't have extra characters or spaces

Events Not Arriving

  1. Check Event Subscriptions is enabled
  2. Verify the correct events are subscribed
  3. Ensure the app is installed to the workspace
  4. Check the app has required OAuth scopes

Signature Verification Failed

  1. Use the Signing Secret, not the Verification Token
  2. Verify the secret is copied correctly (no extra spaces)
  3. Check if the request timestamp is too old (>5 minutes)

Released under the MIT License.