Webhook bots
A webhook bot posts into a room whenever something sends it an HTTP request. Use it for deploy notices, build results, alerts, form submissions, or anything else that can call a URL.
Set one up
- Open a room you created. Only a room's creator can add bots to it.
- Tap the room name at the top, then Bots.
- Under Available, find Webhook and tap Install.
- The bot joins the room and its webhook URL appears. Copy it.
The bot shows in the member list with a small robot mark and posts under its own name and photo. Change its name with the gear icon on the Bots page; add a photo from Profile โ Bots.
Your webhook URL
Each room a bot is in gets its own URL. It looks like this:
https://<your-gabbex-api>/api/hooks/<install-id>/<token>The URL is the password. There is no other sign-in: anyone who has it can post into the room as the bot. Keep it in a secret store (GitHub Actions secrets, your CI's protected variables, an environment variable), not in code you commit.
You can see and copy the current URL any time from the bot's settings (gear icon on the room's Bots page). If it leaks, tap Make a new URL: the old one stops working immediately.
Send a message
Send a POST request with a JSON body. The simplest body has one field:
{
"text": "Deploy finished for v2.4.1 ๐"
}| Field | Type | Details |
|---|---|---|
text | string | The message to post, up to 2,000 characters after trimming spaces. Line breaks are kept. If text is present it always wins over a template. |
- Method:
POSTonly. - Body: any valid JSON object, up to 64 KB. Sending
Content-Type: application/jsonis good practice. - No extra headers or sign-in are needed; the URL is the credential.
- Mentions: write
@nicknameand anyone in the room by that nickname is mentioned and notified, exactly as if a person had typed it. - Formatting: messages are plain text. Emoji work.
Templates
Many services (GitHub, monitoring tools, form builders) send their own JSON and can't be told to send text. For those, set a template in the bot's settings. When a request has no text field, the bot fills the template from the request's JSON and posts the result.
Placeholders are field names in double braces. Use dots to reach into nested objects:
Template: Deploy {{status}} on {{build.branch}} by {{build.author.name}}
Request: {
"status": "succeeded",
"build": { "branch": "main", "author": { "name": "ana" } }
}
Posts: Deploy succeeded on main by ana| Value in the JSON | What the placeholder becomes |
|---|---|
| String | The text as is. |
Number or true/false | As written in the JSON, e.g. 42, 3.5, true. |
Missing, null, object or array | Nothing (an empty string). |
- Field names may use letters, digits and underscores, e.g.
{{head_commit.message}}. Spaces inside the braces are fine. - Array items can't be picked by position; point at a field that holds a single value.
- Templates can be up to 500 characters. The finished message is trimmed and cut at 2,000 characters.
- If the finished message is empty (every placeholder was missing and there's no other text), nothing is posted and you get a 400.
Responses
Every response is JSON. Errors carry a message explaining what went wrong.
| Status | Body | Meaning |
|---|---|---|
200 | { "ok": true } | Posted. |
400 | { "message": "โฆ" } | The body isn't JSON, is over 64 KB, text is over 2,000 characters, the message came out empty, or there's no text and no template is set. |
403 | { "message": "This bot is disabled." } | The bot is switched off in this room. |
404 | { "message": "Not found." } | The URL is wrong, was replaced with a new one, or the bot was removed from the room. |
429 | { "message": "โฆ" } | Too many requests. Wait the number of seconds in the Retry-After header, then try again. |
Only retry on 429 and on network errors or 5xx. A 400, 403 or 404 won't succeed on a retry; fix the request or the URL first.
Limits
- 30 requests per minute per webhook URL. Requests over the limit get a
429and are not posted. Each URL has its own limit, so one busy webhook never slows another. - 64 KB request body.
- 2,000 characters per message.
- 10 bots per room.
If you expect bursts (for example a big CI matrix), combine results into one message instead of posting each one.
Examples
In each example, $GABBEX_WEBHOOK_URL holds the URL you copied.
curl
curl -X POST "$GABBEX_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{"text": "Backup completed in 4m 12s"}'JavaScript (Node 18+ or the browser)
const res = await fetch(process.env.GABBEX_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: 'New signup: @ana just joined ๐' }),
});
if (!res.ok) console.error(res.status, await res.json());Python
import os, requests
res = requests.post(
os.environ["GABBEX_WEBHOOK_URL"],
json={"text": "Nightly report is ready"},
timeout=10,
)
res.raise_for_status()GitHub Actions
Save the URL as a repository secret named GABBEX_WEBHOOK_URL, then add a step:
- name: Tell the room
if: always()
run: |
curl -sS -X POST "$URL" \
-H "Content-Type: application/json" \
-d "{\"text\": \"${{ github.repository }} ${{ github.ref_name }}: ${{ job.status }}\"}"
env:
URL: ${{ secrets.GABBEX_WEBHOOK_URL }}GitHub repository webhooks (with a template)
GitHub sends its own JSON, so use a template. In the bot's settings set the template to:
{{pusher.name}} pushed to {{repository.full_name}}: {{head_commit.message}}Then in GitHub open Settings โ Webhooks โ Add webhook, paste the URL as the payload URL, choose application/json, and select Just the push event. Each push posts a line such as ana pushed to acme/site: Fix the header on mobile.
Monitoring and alerts
Most uptime tools let you edit the JSON they send. Point them at the URL and send a text field:
{
"text": "๐ด api.acme.com is down (HTTP 503) โ @oncall please look"
}Manage the bot
Open the room, tap the room name, then Bots. Each installed bot has:
- On/off switch: while off, the bot leaves the room and its URL answers
403. Switch it back on and it rejoins with the same URL. - Gear icon: change the bot's nickname, copy the current URL, set the template, or make a new URL.
- Trash icon: removes the bot from the room. The URL stops working; messages it already posted stay.
Renaming a bot updates it everywhere it posts, and the room sees a "now known as" line, like a nickname change.
Troubleshooting
- 404 Not found: copy the URL again from the bot's settings. A new URL replaces the old one, and removing the bot from the room retires it.
- 403 This bot is disabled: the bot is switched off on the room's Bots page.
- 400 Body must be JSON: send a JSON object, not form data or plain text. Check your quotes when building JSON in a shell.
- 400 โฆ or set a template: the request had no
textfield. Add one, or set a template in the bot's settings. - Posted message is empty or missing parts: a placeholder doesn't match the JSON. Field names are case-sensitive and nested fields need the full dotted path.
- A mention didn't notify anyone: the nickname must match someone who has joined the room.
- 429 Too many requests: wait for
Retry-Afterseconds, and batch updates where you can.