A webhook is a secret address: a program that sends an HTTP request to it posts a message into a room in Anten. Your build server can say “build finished”, your monitoring tool can report an outage. The program needs no Anten account.
What it is for
A webhook works one way: the program writes into the room, but cannot read it or answer anyone. The message shows under the name you gave the webhook, with a BOT tag.
If the program has to read messages, answer commands or join a voice room, use a bot account instead (see the bots guide).
Make a webhook
- Open the room’s settings. Right-click the room in the room list and choose Room settings, or press the settings icon in the room’s header.
- Under Integrations, choose Webhooks. Only those with the Manage webhooks permission in that room see this part. Administrators have it.
- Enter a name and press Make a webhook. The name may be 1–32 characters. The program’s messages show under it; for example “Builds”.
- Copy the address shown and keep it somewhere safe.
The address is shown only this once. Right below it there is a ready
curlcommand with that address.
The address has this form:
https://anten.example.org/api/v1/webhooks/<id>/<token>
The token at the end is the webhook’s password. Anyone who knows the whole address can write into that room.
Send a first message
-
Put the address in an environment variable. All the examples read it from
ANTEN_WEBHOOK, so the address is never written into code.export ANTEN_WEBHOOK='https://anten.example.org/api/v1/webhooks/<id>/<token>' -
Send a message. The request is JSON with a single field,
content.curl -H "Content-Type: application/json" -d '{"content":"Build finished"}' "$ANTEN_WEBHOOK" -
Look at the room. The message shows under the webhook’s name with a BOT tag. On success the server answers
200with the new message.
Send from a program
Both examples use only the language’s own libraries and print the status and the error’s code if something goes wrong.
Node.js (18 or later)
const res = await fetch(process.env.ANTEN_WEBHOOK, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: 'Build finished' })
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
console.error('Webhook error:', res.status, err.code);
}
Python
import json, os, urllib.error, urllib.request
req = urllib.request.Request(
os.environ["ANTEN_WEBHOOK"],
data=json.dumps({"content": "Build finished"}).encode(),
headers={"Content-Type": "application/json"},
)
try:
urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
print("Webhook error:", e.code, e.read().decode())
Messages and limits
What can be sent
- Text only: 1–4000 characters. Spaces at the start and end are dropped.
- Formatting such as bold, italic, code and quotes shows as in other messages. HTML is never rendered.
- Files, buttons and cards cannot be sent. A request with any field other than
contentis refused.
The name
Messages always show under the webhook’s name; a request cannot change it. If you give the webhook a new name with Rename, it shows only on messages from then on. Earlier messages keep the name they were posted under.
Limits
- A webhook may send 10 messages in a row, then one a second.
- All webhook requests from one IP address are limited too: 30 in a row, then 5 a second. Requests with a wrong token count as well.
- A request over the limit gets
429. Wait as many seconds as theRetry-Afterheader says, then try again. - A room can have up to 10 webhooks. A request body may be at most 16 KB.
What happens to the messages
A webhook’s messages cannot be edited. Those with the Manage messages permission in the room can delete them. When a webhook is deleted, the messages it sent stay in the room. The Webhooks list also shows when each webhook was last used.
Security
Keep the address like a password
Anyone who knows the address can write into that room without an account. Do not put it in a code repository, a chat or an error log. Give it to the program through an environment variable or the secret settings of the system you use.
The server keeps only a digest (SHA-256) of the token, not the token itself. That is why the address cannot be shown again later. The token is not written to the server’s logs either.
Renew or delete the address
If you lost the address or suspect it has leaked, press Renew address next to the webhook. The old address stops working at once, and the new one is shown once. Give the program the new address.
If you no longer need the webhook, press Delete. Making, renaming, renewing and deleting are recorded in the audit log.
Why not in encrypted rooms
In an end-to-end encrypted room, only the devices of the people in it encrypt and open messages; the server does not hold the room’s key. A webhook’s message is written by the server, so it cannot encrypt it. An unencrypted message is not let into an encrypted room either.
So a webhook cannot be made in an encrypted room. If a room is made encrypted later, its webhooks get
409 room_sealed, and the room’s Webhooks part says so with a notice.
Reference
Request
POST /api/v1/webhooks/<id>/<token>
Content-Type: application/json
{ "content": "Build finished" }
No session or other header is needed. A successful request answers 200 with the new message as JSON (id,
room_id, content, webhook, created_at and other fields).
Error responses
An error response carries a code field and a short explanation (message).
| Status | code |
Meaning |
|---|---|---|
400 |
invalid_content |
The message is empty or longer than 4000 characters |
400 |
invalid_json |
The body is not valid JSON, or has a field other than content |
401 |
invalid_webhook |
The address is wrong or was renewed, or the webhook was deleted |
409 |
room_sealed |
The room is end-to-end encrypted |
409 |
text_disabled |
Text chat is off in the room |
413 |
body_too_large |
The body is larger than 16 KB |
415 |
unsupported_media_type |
The Content-Type header is not application/json |
429 |
rate_limited |
Too many requests; wait as Retry-After says |