Skip to content
Chatto v0.5.0

Bots & Webhooks

A bot is a visible user account for an unattended integration. It uses the normal public ConnectRPC API and realtime protocol. A bot starts with no permissions and cannot sign in through the login forms.

  1. Open Server Admin → Bots and select Create Bot.
  2. Enter a username and a display name.
  3. Copy the API key before you close the dialog. Chatto cannot show it again.
  4. Open the detail page of the bot and select Permissions. Enable the server, room-group, or room permissions that the integration needs.
  5. Enable room.join for the rooms that the bot needs. In the Joined row at the top of the matrix, select each room to add the bot, and confirm. Clear a room to remove the bot.

On a new server, only the admin role has bot.create. To let members create bots, allow bot.create for everyone or for a role. A human owner can always manage their bots. A bot with an explicit bot.create grant can create a bot for its own human owner. The new bot starts without permissions. Bot creators need bot.manage for later management. Accounts with bot.manage can manage all bots and can select Reassign owner to give a bot to another active human account. Reassignment keeps the permissions and API keys of the bot. If the previous owner must lose access, create a new key and revoke the old one.

When you delete the owner account, Chatto also deletes all bots that the account owns. Reassign long-lived bots first.

Bots do not inherit everyone, named roles, or the roles of their owner. The bot matrix is an allowlist. A permission is effective only while the owner also has it at that scope. A lock icon marks a permission that the owner ceiling blocks. If the owner loses a permission, the bot also loses it. Bots do not need privileged mode, and they keep working while the owner is signed out.

To grant a permission that requires elevation, activate privileged mode. You must have that permission active at the target scope. Removal of a grant does not require activation.

Room membership alone does not let a bot read messages. Grant one read permission:

  • message.read reads all messages in the room. It includes message.read-interactions.
  • message.read-interactions reads only complete threads that the bot started, where another account mentioned it directly, or where it received a DM. A mention also gives access to older messages in that thread.

Grant one post permission:

  • message.post posts root messages and thread replies.
  • message.post-in-thread posts replies in all readable threads.
  • message.post-in-interactions posts replies only in the threads that message.read-interactions allows.

For a bot that reads all messages but replies only when someone speaks to it, grant message.read and message.post-in-interactions.

You can add your own bots to rooms without room.manage. Room suspensions and archived rooms prevent joining. Universal-room membership is automatic.

Send the API key as a bearer credential:

Authorization: Bearer cht_BK_…

Requests use the bot’s permissions and its owner’s current authority. Grant bot.manage only when you intend to delegate management of other bots, including issuing their credentials. Bots cannot acquire human passwords, sessions, OAuth tokens, verified email addresses, or external logins.

See the BotService reference for API methods.

A bot can have at most 20 active API keys. Create one named key for each integration. To replace a key, create a new key, move the integration to it, and revoke the old key. Revocation immediately makes only that key invalid and closes its realtime connections.

The Integrations page of the bot shows when each key was created and approximately when Chatto last recorded its use. No use recorded does not prove that nobody used the key.

A bot can start a DM with RoomService.StartDM when it has message.post at the Direct messages scope. It can also use this method to find an existing DM without posting permission. To let a bot talk in DMs without channel access, grant message.read and message.post at the Direct messages scope. The owner must have the same permissions at that scope. For a bot that only replies, message.read-interactions at this scope is enough to read its DMs and receive their outbound webhooks.

An incoming webhook lets an external tool post as the bot without an API key.

  1. Open the detail page of the bot, select Integrations, and then select Create Webhook.
  2. Enter a name that identifies the caller. Optionally, select a destination channel room. Chatto adds its room ID to the URL.
  3. Copy the complete URL before you close the dialog. Chatto cannot show it again.

Send JSON with Slack-compatible text and channel fields:

POST /webhooks/incoming/YOUR_WEBHOOK_CREDENTIAL
Content-Type: application/json
{"text":"Deployment finished","channel":"YOUR_ROOM_ID"}
  • Chatto also accepts message or body for the text, and room_id for the room. You can put room_id in the URL query string.
  • Every request needs a room ID. Use the ID, not the room name. If you send more than one text field or room ID, all non-empty values must match.
  • Set "create_thread": true to start a thread in a channel room that supports threads.
  • The bot must be a member of the room and have message.post. A webhook can post to an existing DM that includes the bot. Use an API key and RoomService.StartDM to create the DM first.
  • A successful request returns HTTP 200 with the body ok. There is no idempotency key, so a retry can post the message two times.

A bot can have at most 20 active webhooks. Create one webhook for each integration. To replace a webhook, create a new one, move the caller to it, and revoke the old one.

ResponseCheck
400 invalid_payloadSend valid JSON with non-empty text, message, or body, and a room ID. Make sure that duplicate fields match. Message size and room policy limits apply.
404 channel_not_foundUse the room ID. Make sure that the bot is a member, and that the bot and its owner can post there.
401 invalid_tokenThe credential is invalid or revoked. Create a new webhook and update the caller.

Grafana’s Webhook integration works without a custom payload. Chatto reads the message field of the default Grafana JSON.

  1. Create a bot, add it to the destination channel room, and grant it message.post. The owner must have this permission too.
  2. Create an incoming webhook named Grafana and select the destination room. Make sure that the URL ends with ?room_id=YOUR_ROOM_ID.
  3. In Grafana Alerting, open Contact points and add a contact point. Select Webhook and paste the Chatto URL. Keep the POST method. Leave Custom Payload unset.
  4. Send a test notification and make sure that it appears in the room. Then select the contact point in an alert rule or a notification policy.

To change the text, use the Message field of the contact point. You can use Grafana notification templates there. Firing and resolved alerts post separate messages. Chatto does not update or group earlier alerts. See Grafana’s webhook documentation.

An outbound webhook sends an HTTP request to your tool when someone speaks to the bot.

  1. Open the bot in Server Admin → Bots and select Integrations. In Outbound webhooks, select Create webhook.
  2. Enter a name, the destination URL, and, if necessary, the complete Authorization header value.
  3. Copy the signing secret if your tool verifies signatures. Chatto shows it only once.

Each enabled endpoint receives new messages, including thread replies, that mention the bot directly or that are in a DM with the bot. A DM that mentions the bot sends one request with both triggers. Messages from the bot, ordinary channel messages, edits, and reactions do not send requests. Notification preferences do not apply.

  • A bot can have up to 20 endpoints, including paused ones.
  • Edit webhook changes the URL and the Authorization header. It keeps the signing secret and cancels queued retries. To change the name or secret, create a new endpoint and revoke the old one.
  • Pause stops new requests and cancels queued retries. Resume does not send messages from the paused period.
  • Revoke webhook removes the endpoint permanently.

Destinations require public HTTPS. localhost and *.localhost also permit HTTP, but only when every resolved address is a loopback address. IP literals such as 127.0.0.1 and other private-network hosts are not permitted.

The Runling bot example receives these requests and replies through the Chatto API.

See the Bot Webhook Reference for payloads, signature verification, delivery retries, and reply handling. Your receiving tool gets message content; check its data policy before you connect it.