Synthetic Industry

Job webhooks-in-slack-events-receiver-with-replay-window · revised 11 October 2026

Receive Slack events in your app: acknowledged in time, verified, and safe to replay

Your app answers each Slack event in time, rejects unsigned or stale requests, and claims each event ID before starting the work, so retries and duplicate deliveries do not start it twice.

You might be seeing

  • The Slack app settings show delivery failures or a warning about the request URL
  • The same event triggers the work two or three times
  • A slow downstream call inside the handler means Slack never gets a reply in time

No passwords, keys, card details or admin invites needed to start.

What usually happened

Slack expects an HTTP success from your handler within three seconds, retries a failed delivery up to three times, the first retry nearly immediately, and signs each request with a timestamp so that old captured requests can be refused. A handler that does its work before replying times out and gets retried, so the work runs again. A handler that notes an event as handled only when its work finishes has the same fault in a smaller form: a retry that arrives while the first job is still running looks new, so two jobs run. A handler that checks the signature after parsing the body, or ignores the timestamp, either rejects genuine events or accepts a replayed one. The fault is timing and verification, not a missing route.

Who it’s for: A founder or product owner whose Slack app sends events to their web app, and who sees missing or doubled actions, errors in the Slack app settings or a subscription that Slack paused.

Usually starts when: Slack reports the request URL as failing, the same action happens twice after a slow response, or the app does real work before replying and Slack times it out.

The result: The app's Slack events endpoint answers the setup challenge, verifies each request's signature on the raw body and refuses requests more than five minutes from its own clock, replies before the slow work starts, and starts the work once for any event identifier: the identifier is claimed in one atomic step before the work is queued, so even two deliveries at the same moment queue one job, and a claim whose work never finished is found and recovered after an agreed timeout.

Check whether this job fits

Answer from what you know about the handler and the Slack app settings. No secrets or code are needed.

Which fits best: a Slack endpoint with slow replies or doubled actions, a Slack endpoint that rejects genuine events as unsigned, or a sender other than Slack?
Is Slack sending event notifications, such as a message posted, rather than button clicks or slash commands?
Does the handler do its real work before it replies to Slack?
Can your developer read the exact bytes Slack sent before they are parsed?
Can the app run a task after it has replied?

Answer the questions to see whether this job fits.

Nothing is sent anywhere until you choose to email us.

Send an enquiry about this outcome

Checks you can run yourself

  1. Read the error Slack shows for your request URL

    Open your Slack app's event subscriptions settings and read the message beside the request URL and any delivery-failure notice. Do not copy tokens.

    Look for: A timeout, an SSL, redirect or HTTP status message. Each points to a different cause; include the wording in your enquiry.

What you get

  • A pull request with the endpoint, the verification, the events store with its claim and recovery sweep, and tests
  • A table of the failure cases tested and the response each returns
  • Redacted evidence of a staging delivery carrying Slack's retry headers (x-slack-retry-num and x-slack-retry-reason), produced as described under delivery steps
  • A note of what the recovery sweep does to your particular action if a worker stops after performing it but before marking it done
  • Undo steps and a note on rotating the signing secret

Included

  • One Slack app's Events API endpoint in one existing web app, including the one-time URL verification handshake
  • Signature verification of the raw request body with the app's signing secret and a timestamp tolerance of five minutes, with a constant-time comparison; an event identifier is claimed only after the signature and timestamp checks pass, so a forged request cannot use one up
  • An immediate success reply with the work moved to a queue or background job your stack already has, or the simplest one we add with your approval
  • An events store with a unique key on Slack's event identifier: each verified event is claimed by one atomic insert (state received) before the work is queued and before the reply is sent, the job marks it done when it finishes, and a delivery that finds an existing claim replies success and queues nothing. If the claim cannot be written, the endpoint returns a server error so Slack retries, not a success it cannot honour
  • Recovery for a crash after the claim: the job is queued from the claim record (or in the same database transaction), workers take a time-limited lease on it, and a sweep re-queues or flags any claim still in the received state after an agreed timeout that is longer than the job's longest run
  • Tests with synthetic signed requests, including two identical deliveries at the same moment and a worker stopped after the claim, and a staging run with a Slack app in a test workspace you control

Not included

  • Interactive components, slash commands and modals: they have their own acknowledgement rules and are a separate job
  • Creating the Slack app, choosing event subscriptions or approving permissions in your workspace
  • Distributing the app in the Slack marketplace
  • Fixing the downstream business action that the event triggers
  • A message-queue platform migration or high-volume scaling
  • Making your downstream action safe to repeat: the claim stops duplicate deliveries starting the work, but a worker that stops after performing the action and before marking it done is re-run by the recovery sweep, so a repeat-safe action (or a protection at the service it calls) is a separate scope unless agreed
  • Any other provider's webhooks (for a non-Slack sender, see the generic signed-webhook receiver job)

How we know it’s done

Agreed with you before work starts. Each check produces evidence you keep.

  1. Slack's setup challenge to the staging endpoint is answered with the challenge value and the Slack app settings show the request URL as verified.

    Evidence: A screenshot of the verified status and the request log line.

  2. A genuine signed event is accepted and a request with a wrong signature, a missing signature or a timestamp more than five minutes from the server's time returns an error and starts no work.

    Evidence: Test output for each of the four requests showing the response, that no job was queued and that no event identifier was claimed by a rejected request.

  3. With the downstream step delayed by ten seconds, the endpoint still replies success within the time Slack allows, and the delayed work completes once.

    Evidence: Request timing, the job record and a count of the resulting action.

  4. Delivering the same event identifier a second and third time one after another, including a delivery carrying Slack's retry headers, replies success and does not repeat the work.

    Evidence: Test output with the retry headers shown, a count of the resulting action before and after, and, for the staging evidence, a note of whether the retry was sent by Slack itself or is a labelled replay.

  5. The same genuine event identifier delivered twice at the same moment, from two simultaneous requests, queues exactly one job and both requests receive a success reply.

    Evidence: Test output from a concurrent run (repeated many times, with the number stated) showing one queued job, one claim row and two success responses every time.

  6. With a worker stopped after the claim and before the job starts, the claim stays in the received state, and after the agreed timeout the sweep re-queues it so the action happens once; a claim for a finished job is never re-queued.

    Evidence: Test output with the claim row's state before and after the timeout, the sweep log line and the count of the resulting action.

  7. With the events store made unavailable, a genuine signed event gets a server error, not a success, and starts no work.

    Evidence: Test output showing the response status and an action count of zero.

Sign-off. You read the failure-case table, the verified status, the test output including the two-at-once and worker-stopped cases, and the note on your action's repeat-safety, then sign off in writing and merge. Payment follows sign-off; your team deploys and holds the production signing secret.

If it fails. If the agreed checks do not pass you do not pay for this fixed scope. If the cause is a gateway that alters the body, no place to run background work or an interactive-component need, we explain the evidence and stop. Wider work needs a new written agreement.

When it fits, and when we stop

It fits when

  • A Slack app already exists in a workspace you control, and a person on your side can change its event subscriptions and read its signing secret
  • The endpoint can be reached over HTTPS from Slack, on a staging copy and in production
  • The handler can read the raw request body before parsing, or that can be arranged in your framework
  • The app has, or may add with your approval, a place to run work after the reply: a job queue, background thread or scheduled worker
  • The app has a database or key store that can enforce a unique key on insert across all its server processes, for the events store
  • Your maintainer can review, merge and deploy the change

We stop and tell you if

  • The endpoint sits behind a gateway that rewrites or re-encodes the body and cannot be changed, so the signature cannot be checked as sent
  • The app must show something to the user within the response, which makes it an interactive component job
  • No background execution is possible and the work must finish before the reply
  • No shared store can make the claim atomic across all the processes that receive Slack's requests
  • The signing secret cannot be placed in your secret store

What could go wrong

Before merge, closing the pull request changes nothing. After merge, reverting the commit restores the previous handler. The events table can be kept or dropped under your retention rules, and the signing secret can be regenerated in the Slack app settings at any time.

Scroll the table sideways to read it all.

RiskHow we handle it
Genuine events are rejected because the body was altered before verification.Verification reads the raw bytes first, and a test sends a genuine signed request through the full framework stack.
Two deliveries of one event arrive together, because Slack's first retry is sent nearly immediately after a missed reply, and both pass a check that is only written when the work ends, so the work runs twice.The event identifier is claimed by one atomic insert (unique key) before the work is queued and before the reply is sent; the second delivery finds the claim, replies success and queues nothing. A test sends two identical deliveries at the same moment.
The process stops after the claim, so the event is recorded but never finished.The job is queued from the claim record or in the same transaction, workers hold a time-limited lease, and a sweep re-queues or flags any claim still in the received state after the agreed timeout. A test stops a worker after the claim.
A worker stops after performing the action but before marking the event done, so the sweep runs the action a second time.This cannot be removed by the events store alone. At agreement we check whether the action is safe to repeat, or whether the service it calls offers its own protection, and the handover states the result. The timeout is set longer than the job's longest run so a healthy slow job is not re-run.
The early reply hides a failure in the background work.Background failures are logged with the event identifier and counted, the event stays in the received state until the job marks it done, and the sweep reports or re-queues it instead of losing it.
Clock drift on the server makes valid requests look old.The tolerance is five minutes either side, a test checks both edges, and the server's time source is noted in the handover.

A reviewer separate from the builder checks the diff, that the signature is checked on the raw body in constant time, that the timestamp window is enforced, that the event identifier is claimed by a single atomic insert only after verification passes, that the recovery sweep exists and that no secret or event body is logged in full. Your authorised maintainer merges.

How we deliver

We arrange the work and independent review, then show you the result against the agreed checks. You keep authority over your systems.

  • Agree the app, the events handled, where background work will run, where the events store lives, the recovery timeout and who rotates the secret
  • Read the current handler, its framework's body handling and the path from request to action, and note whether the action can safely run twice
  • Write the failure-case table first: wrong signature, old timestamp, repeated event, two deliveries at once, worker stopped after the claim, store unavailable, handshake, slow work
  • Build the verification, atomic claim, early reply, background hand-off, lease and recovery sweep on a branch
  • Run the cases with synthetic signed requests, including the two-at-once and worker-stopped cases; then produce the retry-header evidence on staging in your test workspace: a temporary build, removed before merge, deliberately replies slower than Slack's three-second limit so that Slack itself sends the retry with its retry headers. If Slack's retry cannot be provoked in that session, a captured delivery is replayed with the retry headers set by hand and the evidence says it is a replay
  • Have an independent reviewer check the diff and evidence, then hand over with undo steps

An enquiry books nothing and charges nothing. Scope, access route and checks are agreed in writing first.

Need to keep it working?

If Slack is how your team runs the business, ask about the monthly service that checks delivery failures, the signing secret and provider changes.

Ongoing work is separately scoped and quoted: no monitoring, response-time guarantee or automatic subscription is included in this job.

Explore an ongoing engineering lane, or mention the responsibility you need in your enquiry.

What you can check

This is a new service. We have not delivered this job for a client yet.

Other ways to get this done

  • Slack's own SDKs for several languages can verify signatures and acknowledge events for you when given the signing secret; ask your developer whether adopting one is practical. docs.slack.dev
  • For a sender other than Slack, the job "A webhook receiver that checks signatures and handles repeats and replays safely" fits better. If your endpoint already exists and the only fault is that it rejects genuine events as unsigned, "Fix one webhook endpoint that rejects genuine events as unsigned" is the smaller repair.

Questions

Why reply before doing the work?

Slack expects a success response within three seconds and retries if it does not get one. Replying first and doing the work after prevents timeouts and the repeats they cause.

How is this different from a general webhook receiver?

It follows Slack's own rules: its handshake, its signing scheme with a timestamp window, its time limit and its retry headers. For a sender other than Slack, the job "A webhook receiver that checks signatures and handles repeats and replays safely" is the fit. If your Slack endpoint already exists and the only fault is that it rejects genuine events, "Fix one webhook endpoint that rejects genuine events as unsigned" is the smaller repair.

Will the work really run only once?

The job starts the work once per event identifier, even when two deliveries arrive at the same moment, because the identifier is claimed in one atomic step first. If a process stops after the claim, a sweep re-queues it after an agreed timeout. If a worker stops after doing the work but before marking it done, the sweep will run it again, so your action should be safe to repeat; we check this at agreement and say so in the handover.

Do you need our Slack signing secret?

No. You put the test app's secret in your staging configuration. The production secret stays with you.

Does it handle buttons and slash commands?

No. Those have different acknowledgement rules and are a separate scope.

Send an enquiry

Send us

  • The app's language and framework, and what the handler does when an event arrives
  • What Slack's app settings show as the error, copied as text without tokens or the secret
  • How long the handler's slowest step usually takes, as best you know
  • Do not send the signing secret, bot tokens, workspace exports or code in the first enquiry

Later, once you agree

  • Read access to the code through a company-controlled repository or export, and a staging environment you control
  • A test Slack app and workspace you own, and its signing secret placed in the staging configuration by you
  • Permission to add a background-work mechanism if none exists, in writing, and a small table or key space for the events store
  • A staging HTTPS address added to the Slack app's event subscription, and someone in the test workspace able to post a test message when asked

You own the Slack workspace, the app and its signing secret. We work on a branch through a company-controlled identity and a test workspace you create. The production secret stays with you.

A public HTTPS link only, without login details, query strings or fragments. No code or logs.

Sending emails your enquiry and contact address to our team through our mail provider (Resend). It is not kept in a website database. Do not send passwords, keys, recovery links, confidential code or customer records. Your contact email is unverified; nothing is ordered, charged or reserved. Privacy notice.

Email fallback: open your mail app

If website submission is unavailable, review and send the fallback email yourself. An email fallback is not a website receipt. Or write to hello@syntheticindustry.ai with “webhooks-in-slack-events-receiver-with-replay-window” as the subject.