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.
textis the message body, rendered through the same Markdown pipeline as chat. An optionalusernameoverrides the display name for that post. - Curl-friendly. A bare form field works too:
A legacy Slackcurl --data-urlencode text='build is green' https://<your-host>/api/webhooks/<id>/<token>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.