Webhooks

Let other tools post messages into a topic or project, or start a flow, with a single HTTP request.

Webhooks let other tools talk to Kluff over plain HTTP. There are two kinds:

KindWhat it doesWhere you set it up
Incoming webhookPosts a message into one topic or one project channelThe topic's Webhooks tab, or Project settings
Flow webhookStarts a flow run with the data you sendThe 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

  1. 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.
  2. Choose Add webhook (or Add, once the list has webhooks).
  3. Give it a name, like GitHub Actions or Sentry. This is the sender name people see.
  4. Optionally pick an avatar: an emoji, or a link to an image (https:// only).
  5. 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.
  6. 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 post
  • icon_emoji (like ":bell:") or icon_url (an https:// image): show a different avatar for this post
  • silent: 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, danger or 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:

ResponseMeaning
200 okPosted
400 invalid_payloadThe body isn't valid JSON (or form-encoded payload=)
400 no_textNothing to show: no text, blocks or attachments
400 invalid_blocks / too_many_attachmentsMore than 50 blocks or 100 attachments
403 no_active_hooksThe webhook is paused
404 no_serviceThe URL is wrong, or the webhook was deleted
404 channel_not_foundThe topic or project no longer exists
410 channel_is_archivedThe topic is archived
413 request_too_largeThe body is over 1 MB
429 rate_limitedToo 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.

  1. Open the flow in Flows, select the Trigger step and choose Webhook.
  2. Save the flow.
  3. In the trigger settings, choose Enable & generate secret.
  4. 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.