Outgoing Webhooks

Outgoing webhooks send event notifications to your URL when things happen in Monotree. Configure each endpoint and the events it should subscribe to in the Open API → Outgoing Webhooks tab.

Available events

EventFires when
monotree.post.createdA post is created.
monotree.comment.createdA comment is added to a post.
monotree.comment.updatedA comment on a post is edited.
monotree.comment.deletedA comment on a post is deleted.
monotree.announcement.publishedAn announcement is published.
monotree.formresponse.createdA form response is submitted.
monotree.formresponse.updatedA form response's status, deadline, hidden/archived flag, or assignees change. Does not fire on initial creation (use formresponse.created) or for unrelated entities like comments. Filterable by form. Whistleblower forms never dispatch.
monotree.user.registeredA user accepts an invite and registers.
monotree.onboarding.completedA user completes onboarding.

Payload

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "event": "monotree.post.created",
  "version": "v1",
  "timestamp": 1711270800,
  "payload": {
    "id": 123,
    "body": "Post content here",
    "wall_id": 1,
    "wall_name": "General",
    "author": { "id": 1, "name": "John Doe", "email": "john@example.com" },
    "created_at": "2026-03-22T10:00:00+00:00",
    "updated_at": "2026-03-22T10:00:00+00:00"
  }
}

Signature verification

Every delivery includes two headers:

HeaderValue
TimestampUnix timestamp at the time of delivery.
SignatureHMAC-SHA256(timestamp + "." + raw_json_body, your_webhook_secret) as a hex string.

Verify both:

  1. Recompute the HMAC using your secret and compare it to the Signature header in constant time.
  2. Reject requests whose Timestamp is more than 5 minutes old to prevent replay attacks.
const crypto = require('crypto');

function verify(req, secret) {
  const ts = req.headers['timestamp'];
  const sig = req.headers['signature'];
  const body = req.rawBody; // the raw, unparsed JSON string
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${ts}.${body}`)
    .digest('hex');
  if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) return false;
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  return true;
}

Authentication

Each webhook can also be configured with one of: Bearer token, Basic Auth, custom API key header, or arbitrary custom headers — useful when your receiver enforces its own auth on top of the signature.

Retries

Failed deliveries (any non-2xx response, or a timeout > 10 seconds) are retried with exponential backoff. Delivery logs are visible in the Outgoing Webhooks tab.