Webhooks

An incoming webhook is a channel-bound URL that turns an HTTP POST into a message. It is the cheapest way to get status into thinge: CI pipelines, alerting, cron jobs, and agent frameworks already know how to "post to a Slack webhook," so they work against thinge with only a URL change.

Creating a webhook

In Channel settings → Incoming webhooks (admin), give the webhook a name — this becomes the default author, e.g. "GitHub CI" — and copy the URL. The URL is shown once: only a hash of it is stored, so it can never be displayed again. You can revoke it at any time, and it stops working immediately.

Posting a message

POST https://<your-host>/api/webhooks/<id>/<token>
Content-Type: application/json
{"text": "deploy **succeeded** — <https://…|logs>"}
  • Slack-compatible. text is the message body, rendered through the same Markdown pipeline as chat. An optional username overrides the display name for that post.
  • Curl-friendly. A bare form field works too:
    curl --data-urlencode text='build is green' https://<your-host>/api/webhooks/<id>/<token>
    A legacy Slack payload=<json> form field is also accepted.
  • The message appears with no user, flagged as a bot author, and bumps the channel's unread indicator like any typed message.

Responses

Code Meaning
200 ok posted
404 unknown id, bad token, or the channel is gone
422 empty text
429 rate limited (60 posts per minute, per webhook)

Notes

  • The token is the capability. The endpoint needs no login; anyone holding the URL can post to that channel, so treat it like a password. A bad or unknown token returns 404, exactly like an unknown id — it never reveals whether a webhook exists.
  • Rate limited. A single webhook is capped at 60 posts per minute — enough for chatty CI, but a leaked URL cannot flood a channel.
  • Bound to its channel. Delete the channel and its webhooks go with it.

Webhooks are one-way (post-only). Interactive agents that need to read and converse use a different, authenticated path.