Webhooks let other tools talk to Kluff over plain HTTP. There are two kinds:
| Kind | What it does | Where you set it up |
|---|---|---|
| Incoming webhook | Posts a message into one topic or one project channel | The topic's Webhooks tab, or Project settings |
| Flow webhook | Starts a flow run with the data you send | The flow's Trigger step |
Use an incoming webhook when people should see something: a deploy finished, an alert fired, a form came in. Use a flow webhook when Kluff should do something with the data.
Incoming webhooks
An incoming webhook is a secret URL. Anything that can send an HTTP POST can use it to post into the channel it belongs to: CI pipelines, monitoring tools, scripts, no-code tools.
The format is the same as Slack's incoming webhooks, so a tool that already posts to Slack usually works if you paste the Kluff URL in place of the Slack one.
Create a webhook
- Open the place the messages should go:
- Topic: open the topic, then the Webhooks tab in the right panel.
- Project: open Project settings and scroll to Incoming webhooks. Posts land in the project's main channel.
- Choose Add webhook (or Add, once the list has webhooks).
- Give it a name, like
GitHub ActionsorSentry. This is the sender name people see. - Optionally pick an avatar: an emoji, or a link to an image (
https://only). - In a topic, turn on Notify members if everyone in the topic should get a notification for every post. Leave it off for noisy feeds; people who are mentioned are notified either way.
- Create it, then copy the URL right away. Kluff shows it only once.
Anyone who can see the topic or project can create a webhook.
Send a message
Send a JSON body with a text field:
curl -X POST -H 'Content-Type: application/json' \
-d '{"text": "Deploy *succeeded* :rocket:"}' \
https://your-webhook-url
Kluff replies ok when the message is posted. The message shows up right away, with an App badge next to the sender name so nobody mistakes it for a teammate.
Format messages
text uses Slack's formatting:
*bold*,_italic_,`code`,```code blocks```and> quotes- Links:
<https://example.com|label>, or a bare<https://example.com> - Emoji shortcodes like
:white_check_mark:and:rocket: - Line breaks are kept as you send them
To post the text exactly as written, with no formatting, add "mrkdwn": false.
You can also set, per message:
username: show a different sender name for this posticon_emoji(like":bell:") oricon_url(anhttps://image): show a different avatar for this postsilent: true: skip the Notify members notification for this post (mentions still notify)
Rich messages
For more structure, send Slack Block Kit blocks or legacy attachments. Kluff turns them into readable messages:
- Blocks: headers, sections with fields, context lines, dividers, images, videos, link buttons, rich text, markdown and tables. Blocks Kluff doesn't recognize still show their text.
- Attachments: each one becomes a card with a colored edge (
good,warning,dangeror a hex color), author, linked title, text, side-by-side fields, image, footer and timestamp.
{
"text": "New error in checkout",
"attachments": [
{
"color": "danger",
"title": "TypeError: cart is undefined",
"title_link": "https://sentry.example.com/issues/123",
"fields": [
{ "title": "Env", "value": "production", "short": true },
{ "title": "Users", "value": "42", "short": true }
],
"footer": "Sentry"
}
]
}
When a message has blocks, text is only used as the notification preview, just like in Slack. Buttons need a URL to show; interactive buttons that call back into Slack are dropped.
Mention people
<@[email protected]>or<@user-id>mentions a member of your workspace<!subteam^group-id>mentions a group<!here>,<!channel>and<!everyone>mention everyone in the topic or project
Mentioned people get a notification if they are members of that topic or project. Mentions of people Kluff doesn't know stay as plain text.
Webhook posts and @kluff
Webhook posts never start Kluff, even if they contain @kluff. Outside tools can't run your agents or use up your workspace's AI usage.
When Kluff later reads the conversation, it sees webhook posts marked as external, so text inside an alert can't give it instructions. To get help with a post, mention @kluff yourself, for example: @kluff what caused the alert above?
Manage webhooks
Each webhook in the list shows its name and when it was last used. Click it to edit the name, avatar or notification setting; the edit dialog also shows who created it and how its URL ends. From the ⋯ menu you can:
- Edit it
- Pause it: the URL stops accepting posts but keeps working once you Resume
- Delete it: the URL stops working immediately
Only the person who created a webhook, or a workspace admin, can change or delete it.
There is no way to view a URL again or rotate it. If a URL leaks, delete the webhook and create a new one. Deleting a topic or project also deletes its webhooks.
Limits and errors
Each webhook accepts about one message per second (60 per minute). A message can have up to 50 blocks and 100 attachments, the body can be up to 1 MB, and long text is cut at 16,000 characters.
Errors come back as a short code in the response body:
| Response | Meaning |
|---|---|
200 ok | Posted |
400 invalid_payload | The body isn't valid JSON (or form-encoded payload=) |
400 no_text | Nothing to show: no text, blocks or attachments |
400 invalid_blocks / too_many_attachments | More than 50 blocks or 100 attachments |
403 no_active_hooks | The webhook is paused |
404 no_service | The URL is wrong, or the webhook was deleted |
404 channel_not_found | The topic or project no longer exists |
410 channel_is_archived | The topic is archived |
413 request_too_large | The body is over 1 MB |
429 rate_limited | Too many messages; wait for the Retry-After seconds |
Start a flow from a webhook
Any flow can be started by an HTTP request instead of by hand or on a schedule.
- Open the flow in Flows, select the Trigger step and choose Webhook.
- Save the flow.
- In the trigger settings, choose Enable & generate secret.
- Copy the Endpoint URL and the secret. The secret is shown only once.
Call it with the secret in the Authorization header (or in X-Kluff-Webhook-Secret):
curl -X POST \
-H 'Authorization: Bearer YOUR_SECRET' \
-H 'Content-Type: application/json' \
-d '{"email": "[email protected]", "plan": "pro"}' \
https://your-flow-endpoint-url
The JSON body becomes the flow's trigger input: later steps read it as {{ trigger.input.email }}, {{ trigger.input.plan }}, and so on. Kluff answers 202 with the new run's run_id, and the run appears in the flow's history.
- Rotate secret issues a new secret; the old one stops working at once.
- Disable turns the webhook off and deletes the secret.
- Run in the builder still works for testing.
The body must be a JSON object up to 1 MB. A wrong or missing secret gets 401; a disabled webhook or unknown flow gets 404.
Keep webhook URLs safe
- Treat incoming webhook URLs and flow secrets like passwords. Anyone who has one can post to that channel or start that flow.
- Store them in your tool's secret settings, not in code or shared docs.
- Give each tool its own webhook, so you can delete one without breaking the others.
Troubleshooting
Nothing shows up, and the response is 404 no_service. Check that you copied the whole URL. If the webhook was deleted, create a new one.
My Slack formatting looks wrong. Make sure the body is JSON with Content-Type: application/json, or form-encoded as payload=<json>. Check that mrkdwn isn't set to false.
People aren't notified. Only mentions notify by default. Turn on Notify members on the webhook, and check the post doesn't set silent: true.
@kluff didn't answer a webhook post. That's intended. Mention @kluff in a reply yourself.
