Synthetic Industry

Troubleshooting guide · updated 2026-10-11

Alerting Slack from your app: webhook or bot token, and how to avoid slowing or flooding it

Choose between an incoming webhook and a bot token, keep alerts outside the customer's request, and respect Slack's per-channel rate limit and Retry-After.

Two ways to post, one choice

An incoming webhook gives you a URL that contains a secret. It is tied to one user and one channel, the channel is chosen by whoever installs it, and you cannot override it in the payload. A bot token with the chat:write permission lets the app choose a channel, provided it is a member or holds the extra permission for public channels. The webhook is the simplest for one alert to one place; the token is better when the destination varies or when you need to see more of Slack's errors.

  • Keep either secret in configuration, never in the repository.
  • A leaked webhook URL lets anyone post to that channel; Slack says it searches for and revokes leaks.
  • Always include a plain text value so notifications and screen readers show the content.

Keep the post out of the customer's request

If the call to Slack sits inside the code that handles a checkout, then Slack's slowness becomes your checkout's slowness and Slack's outage becomes a failed sale. Record the event first, then post from a background job or after the response is sent. If the alert job retries, the same event posts again unless you record that it has been posted, keyed on the event's identifier, before or as you send.

  • One alert per event ID, recorded in your own data.
  • A failed post must never fail the customer's action.
  • Count failed posts somewhere a person will see them.

Respect the rate limit

Slack documents roughly one message per second per channel for posting, tolerating short bursts but without guarantees about what is shown during them. When a limit is exceeded, Slack answers with HTTP 429 and a Retry-After header giving the number of seconds to wait. A loop that retries at once makes it worse. Wait the stated time, then try again, and if a burst of events is routine, combine them into one summary message.

  • Read Retry-After as seconds and wait at least that long.
  • Stop retrying after an agreed number of attempts and flag the alert as unposted.
  • Channel messages are not paging: nothing guarantees a person sees them.

Errors worth recognising

A webhook that returns no_service or no_active_hooks has been disabled or removed, and retrying will not help; the owner must create a new one. An archived channel and a missing membership produce their own error strings. The documentation also lists action_prohibited for an admin restriction. Log the error string, never the URL or token, and alert a person when the same error repeats, because silent failure is the way most alerts are lost.

How the paid outcome is accepted

The Slack alert outcome is accepted when one staging event posts one message with the agreed fields, a repeated event posts nothing, a stubbed rate-limit response with a five-second wait delays the post without touching the customer's request, and an error is logged and counted. The fixed £195 price is untested and payment follows sign-off.

Sources and limits

  • Slack: incoming webhooks Checked 2026-10-11.
    • The URL contains a secret and is specific to one user and one channel; channel and username cannot be overridden.
    • Failures return error strings such as no_service, channel_is_archived and action_prohibited.
    • Slack searches for and revokes leaked secrets.
  • Slack: chat.postMessage Checked 2026-10-11.
    • It needs the chat:write scope; chat:write.public lets an app post to public channels it has not joined.
    • With blocks, text is the fallback for notifications and screen readers.
    • Errors include not_in_channel, channel_not_found, is_archived and ratelimited.
  • Slack: Web API rate limits Checked 2026-10-11.
    • chat.postMessage allows roughly one message per second per channel with short bursts tolerated.
    • A 429 response includes a Retry-After header with seconds to wait.