Skip to content

Room webhooks

Rooms speak both directions: an incoming webhook lets another system post into a room, and an outgoing one forwards what is said to a URL you control.

An incoming webhook lets a script post a message into a room — CI results, deploy notifications, alerts.

Create one on the room’s Settings → Webhooks tab: give the key a name (that’s the name messages will display under) and it’s created immediately. Each key gets its own page with the full key (masked until you press Reveal), the ingest URL, a ready-made curl example, and buttons to rotate, disable, or delete it.

Terminal window
curl -X POST \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: my-unique-id-001' \
-d '{"body": "hello from a script", "sender": "my-bot"}' \
https://vroxy.ai/wh/in/YOUR_KEY

Request fields:

Field Meaning
body The message text — markdown and @-mentions work. Required. Slack-style text is accepted as an alias; body wins if you send both.
sender Per-request display-name override, up to 120 characters (aliases: name, username). Falls back to the key’s name, then the key creator’s account name.
room Post into a different room by its id (aliases: room_hashid, channel). Only rooms the key’s creator has joined and can see — never a DM.
idempotency_key Retry-safe deduplication, up to 255 characters. A replay answers 200 with the original message id instead of posting a duplicate. The Idempotency-Key header wins over the field.

Responses: 201 with {"message_id": "…"} on success, 404 for an unknown, disabled, or deleted key (all three answer identically, so the key’s existence never leaks), 422 for a missing or over-long body, and 429 when rate-limited — 60 posts per minute per key by default.

Things to know:

  • The path token is the authentication. There are no headers to set and no CSRF token. Treat the URL like a password: anyone holding it can post into that room.
  • Permissions resolve against the seat of the person who created the key, so if they leave the room (or the workspace) their keys stop working.
  • Webhook messages display under the key’s name with a 🔗 badge, and notify everyone in the room — including the key’s creator.
  • Rotate mints a fresh key and the old one stops working immediately.

An outgoing webhook POSTs every member-posted message in the room to a URL of yours. Add one on the same Settings → Webhooks tab.

The body is JSON:

{ "event": "message_created", "room": { … }, "message": { … } }

and it carries a signature header computed over the raw request body with the hook’s signing secret:

X-Vroxy-Signature: sha256=<HMAC-SHA256 of the body>

Verify it before trusting the payload. The webhook’s page shows the secret (masked until revealed), the payload shape, and a Ruby verification snippet.

Two behaviors to plan around:

  • Loop guard. Messages that arrived via an incoming webhook are never fanned out to outgoing webhooks. Without that, an incoming hook feeding an outgoing hook that posts back is a message storm.
  • Auto-disable. A hook that fails 20 times in a row disables itself, so a dead endpoint doesn’t burn a delivery attempt per message forever. The room’s webhook list shows the consecutive-failure count; fix the endpoint and re-enable it.