The loop
Create an endpoint and it prints a permanent ingest URL. Everything sent there is captured whole — headers, body, the exact bytes — before anything else happens. Stream those events to a port on your machine, and replay any one of them as often as you like while you fix the handler. Forwarding runs from your own machine, so it isn't a billable delivery.
What's different about Slack
Finish typing a Request URL into Event Subscriptions and Slack immediately POSTs a url_verification body carrying a challenge string; the form refuses to save until your endpoint answers 200 with that same string echoed back. A capture-everything tunnel that acknowledges with an empty body fails the check, and you never get past the form. webhook.co's ingest recognizes the handshake, echoes the challenge, and files nothing, so the green check mark appears and your event list stays free of setup noise.
Where the URL goes
Your app's settings live at api.slack.com/apps. Open the app, find Event Subscriptions, and flip the toggle on; a Request URL field appears, and that's where the ingest URL goes. Slack fires its challenge the moment you stop typing, and a green check mark means it took. If your endpoint was slow to wake and the first attempt timed out, the Retry button re-runs it. Slack also treats Request URLs as case-sensitive.
Slack documents no send-a-test-event control and no delivery log to redeliver from. The one request you can trigger on demand is the handshake, via that Retry button. Everything else means doing the real thing in a workspace: mention the app, post in a channel it watches, react to a message. Nothing reaches you until the Request URL clears its handshake, so get the green check mark first.
What Slack sends
Identifiers are lowercase and underscore-separated, shaped object then action. Messages are the exception worth knowing: instead of a single message event there are dot-suffixed subscriptions per conversation type, so a DM and a public channel post are separate subscriptions.
- app_mention
- message.channels
- message.im
- reaction_added
- member_joined_channel
- team_join
Worth knowing first
Three seconds, then three retries
Slack wants a 2xx inside three seconds. Miss it and the attempt counts as failed, after which Slack retries nearly immediately, then after a minute, then after five. Each attempt carries x-slack-retry-num and x-slack-retry-reason, whose documented values include http_timeout, too_many_redirects and ssl_error. A non-200 carrying x-slack-no-retry: 1 tells Slack to abandon that one event.
Slack can switch you off
Cross more than 95% failed delivery attempts inside a 60-minute window and Slack temporarily disables the app's event subscriptions. The app's creator and owner get an email about it, and turning subscriptions back on is a manual trip through Slack app management. Apps taking fewer than 1,000 events an hour are exempt from the automatic disable.
Duplicates are your problem
Slack's docs never commit to a delivery model, so treat the retry schedule as your duplicate story: a 2xx that arrives late earns you the same event twice. The outer wrapper carries an event_id that Slack documents as globally unique across all workspaces. It is the obvious dedup key, though Slack never nominates it as one.
A ceiling on hourly deliveries
Deliveries top out at 30,000 per workspace per app per 60 minutes. Past the line Slack stops delivering and sends an app_rate_limited event for each minute you stay throttled; what becomes of the events above the cap is left unstated. Unlikely to bite in development, unpleasant to meet later.
Signature checking is handled for you once the secret is registered — you can also check a captured signature by hand in the signature verifier.