Events (webhooks)
With events, Catita notifies your bot when something happens in the server, so it doesn’t have to keep asking.
Set them up
Section titled “Set them up”In /settings > API > Add webhook, enter your bot’s URL (it must be https and public). Catita shows you the signing secret (whsec_…) once: save it. Up to 3 webhooks per server.
On the webhook’s screen you pick the events, test it with Test (sends a ping), change the secret or turn it off.
| Event | When | data |
|---|---|---|
level.up |
Someone levels up | userId, instance, level, oldLevel |
level.rebirth |
Someone rebirths | userId, instance, rebirths, level |
streak.updated |
Someone extends their streak | userId, instance, current, best |
customrole.key_redeemed |
Someone redeems a custom role key | userId, key, days |
economy.wallet_changed |
The API changes a wallet | userId, instance, amount, wallet, keyId |
What arrives
Section titled “What arrives”A JSON POST:
{ "id": "6f1c…", "type": "level.up", "guildId": "111111111111111111", "createdAt": "2026-11-04T18:00:00.000Z", "data": { "userId": "123456789012345678", "instance": "1", "level": 12, "oldLevel": 11 }}With these headers:
Catita-Event: the type.Catita-Delivery: the eventid(if it arrives twice, ignore the repeat).Catita-Timestamp: Unix seconds of the delivery.Catita-Signature:v1=plus the hex HMAC-SHA256 of<timestamp>.<body>with your secret.
Answer with a 2xx within 5 seconds. If it fails, Catita retries 5 more times with growing waits (10 s, 20 s, 40 s…). After 25 failures in a row it turns the webhook off and posts in the log channel. Redirects are not followed.
Verify the signature
Section titled “Verify the signature”Always check the signature and that the timestamp is recent, so you know the notification comes from Catita:
const crypto = require('node:crypto');const express = require('express');
const app = express();
app.post('/catita', express.raw({ type: 'application/json' }), (req, res) => { const timestamp = req.get('Catita-Timestamp'); const given = req.get('Catita-Signature') || ''; const expected = 'v1=' + crypto .createHmac('sha256', process.env.CATITA_WEBHOOK_SECRET) .update(`${timestamp}.${req.body}`) .digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300; const valid = given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected)); if (!fresh || !valid) return res.sendStatus(401);
const event = JSON.parse(req.body); if (event.type === 'level.up') { console.log(`${event.data.userId} reached level ${event.data.level}`); } res.sendStatus(204);});
app.listen(3000);Use the body exactly as it arrives (don’t re-serialize it), or the signature won’t match.
Catita is a project by Random Dev, a development studio owned by ZENU GROUP LLC. catita.gg
