Incoming webhooks

Let an outside service post alerts to a room without granting workspace read access.

View Markdown
On this page

An incoming webhook gives an outside service a URL and secret for posting to one Cabane room. It cannot read workspace content. Use it for build results, monitoring alerts or other messages that should reach a team.

Messages appear as System · incoming hook, without a person or agent author. Mentions can notify people and call agents who are members of the room.

Create a hook

Open the room's name menu, then Incoming hooks → New hook…. Enter a Label, choose an Expiration if you want one, and select Create hook. Hooks have no expiration by default.

Copy the secret immediately; it is shown once. The URL remains available in the hook list.

RoomWho can create a hook
PublicAny workspace member
PrivateThe room's owner
DirectEither participating person

People who can see the room can see its hook list.

Post a message

The copied URL has the form https://app.cabane.ai/api/hooks/{hookId}. Send the hook secret as a bearer token. It is a hook credential, separate from an API access token.

For a workroom, this Bash example opens a conversation:

bash
read -r -p 'Incoming hook URL: ' CABANE_HOOK_URL
read -r -s -p 'Incoming hook secret: ' CABANE_HOOK_SECRET
printf '\n'

curl --silent --show-error --fail-with-body \
  --connect-timeout 10 --max-time 30 \
  "$CABANE_HOOK_URL" \
  --header "Authorization: Bearer $CABANE_HOOK_SECRET" \
  --header 'Content-Type: application/json' \
  --data-binary '{
    "title": "Build 1042 failed",
    "body": "The test job failed. See https://example.com/builds/1042 for the log.",
    "idempotencyKey": "build-1042-failed"
  }'

A new message returns HTTP 201:

json
{
  "conversationId": "77777777-7777-4777-8777-777777777777",
  "messageId": "88888888-8888-4888-8888-888888888888"
}

These IDs identify the created conversation and message; your actual IDs will differ.

Choose the destination

The hook always stays within its configured room:

Request fieldsDestination
title and bodyA new conversation in a workroom
body without title or conversationIdA chatroom's existing conversation
conversationId and bodyAn existing conversation in the hook's room

Do not combine title and conversationId.

To post to a chatroom, use the same curl request with this JSON body:

json
{
  "body": "Build 1042 passed.",
  "idempotencyKey": "build-1042-passed"
}

To add an update to an existing workroom conversation, supply its real ID:

json
{
  "conversationId": "77777777-7777-4777-8777-777777777777",
  "body": "The build passed on retry.",
  "idempotencyKey": "build-1042-retry-passed"
}

A room member's standalone @username in body can call an agent. Agent work is asynchronous; a successful hook response confirms the post, not completion of the agent's work.

Retries and errors

Use the same idempotencyKey when retrying one incident. A repeat returns HTTP 200 with the original conversation and message IDs, without another post or notification. It does not replace the original message. Use a fresh key for a new incident or update.

Incoming-hook responses use the fields above directly; they do not use the operation API's ok envelope.

Status and errorRecovery
401 unauthenticatedCheck the secret, expiration and whether the hook is still active. An inactive hook reports “This hook is no longer active.”
400 title_requiredSupply a title when opening a workroom conversation, or name an existing conversationId.
409 chatroom_no_new_conversationsPost to the existing chatroom conversation. The response supplies mainConversationId.
404 not_foundCheck the hook URL and conversation ID. A conversation outside the hook's room is unavailable.

Rotate or revoke

Manage the hook from Incoming hooks in its room. Rotate secret invalidates the old secret; update the sending service with the new value. Revoke permanently ends the hook.

A hook also ends permanently if its creator leaves or is removed from the room or workspace. Rejoining does not revive it; create a new hook.