Reference

Slack webhooks

Operational reference for the Slack incoming webhook integration, request contract, error codes, retry semantics, rate-limit handling.

Request contract

RouteForms sends a single POST per matched routing rule. The Slack incoming webhook URL takes the form https://hooks.slack.com/services/T.../B.../... and accepts a JSON body.

POST https://hooks.slack.com/services/T0/B0/XXX
Content-Type: application/json

{
  "text": "...",
  "icon_emoji": ":inbox_tray:",
  "username": "RouteForms",
  "blocks": [ ... ]   // optional Block Kit array
}

The minimum body is {"text": "..."}. Anything else — icon, sender name, Block Kit, attachments, is optional and overrides Slack's defaults for that channel.

Success response

On success, Slack returns HTTP 200 with the body literal ok. RouteForms records this in the delivery log as DELIVERED.

Error responses

Slack returns specific error strings depending on the failure mode. The most common ones:

  • invalid_payload(HTTP 400). JSON didn't parse, or a required field is missing. Almost always a template-editing typo.
  • channel_not_found (HTTP 404), channel was deleted or archived after the webhook was created. Webhook needs to be re-bound.
  • no_service (HTTP 404), webhook URL revoked at the workspace level. Likely a new URL was issued or the admin disabled it.
  • rate_limited (HTTP 429), too many posts to the same workspace in a short window. Slack supports a burst budget; retry with backoff (RouteForms does this automatically).
  • 5xx. Slack-side transient failure. RouteForms retries with exponential backoff.

Retry policy

RouteForms retries the following responses with exponential backoff: 2s → 6s → 18s → 54s → cap. The final cap means up to ~80 seconds of retries before the delivery is marked FAILED.

  • 5xx, yes, retry.
  • 429 (rate-limited), yes, retry.
  • 4xx other than 429, no, no retry (it's a permanent failure).

Idempotency on the Google Forms response ID guarantees that retries can't produce duplicate posts even under partial network failure. See retries & idempotency reference.

Rate limits

Slack's published limit for incoming webhooks is approximately 1 message per second per webhook URL, with a short burst budget. In practice, occasional bursts above that rate succeed; sustained high-volume posts trigger 429s and force RouteForms's retry backoff.

For forms posting at >30 submissions/minute sustained, contact us — we can shard the workload across multiple webhook URLs bound to the same channel, which is the standard high-volume pattern.

Webhook creation

The standard creation path inside Slack:

Slack → Workspace name (top-left)
  → Settings & administration → Manage apps
  → search "Incoming Webhooks"
  → Add to Slack
  → Pick destination channel
  → Copy URL

The URL is the secret. Anyone with the URL can post to the bound channel. Treat it as sensitive (RouteForms stores it encrypted; rotate via the Slack app settings if exposure is suspected).

Channel binding

Each webhook URL is bound to exactly one channel at creation time. The binding can't be changed after the fact, to redirect to a different channel, create a new webhook bound to that channel.

For private channels, the creator must be a member at creation time. RouteForms then posts as the webhook's integration identity, not as the creator; the creator can leave the channel later and posts continue.