Gareth Wilson Gareth Wilson

Guide to JumpCloud Webhooks: Features and Best Practices

Published


JumpCloud webhooks send directory and device alerts to the tools your team already works in. A device runs low on disk space, unauthorised software is detected, a Directory Insights rule matches a suspicious event, or someone files an access request: a webhook channel can push each of those to a SIEM, a ticketing system or your own service as it happens.

This guide covers how JumpCloud webhook channels are set up and attached to rules, what the alert payload contains, how to authenticate requests with Basic authentication, the retry behaviour, and the best practices for production.

What are JumpCloud webhooks?

In JumpCloud, a webhook is a notification channel: a URL plus authentication and headers, created once in the Admin Portal and then attached to whatever should notify it. Three kinds of events can use a channel:

  • Alert notifications from alert rules, such as low disk space or unauthorised software.
  • Directory Insights events, through an Insights rule.
  • Access Management events, chosen in the Access Requests settings without a separate rule.

A channel can be reused across many rules and services. JumpCloud sends each notification as a POST with a JSON body, using a fixed schema per event type.

JumpCloud doesn't sign webhook requests. A channel can authenticate with Basic Authentication (a username and password), Token Authentication, or custom static headers. Basic Authentication is the one this guide uses and the one Hookdeck's JumpCloud source checks. JumpCloud doesn't document which header or scheme Token Authentication uses.

JumpCloud webhook features

FeatureDetails
ConfigurationAdmin Portal: Settings > Notification Channels > Webhooks
TriggersAlert rules, Insights rules and Access Request events
MethodPOST with a JSON body
PayloadFixed JSON schema per event type, not customisable
AuthenticationNone, Basic Authentication, Token Authentication, or up to 25 custom headers
SignatureNone
URL secretsUp to 10 sensitive values in the URL can be masked and encrypted
RetriesThree, with exponential backoff, on 5xx, 408 or 429
Failure loggingFailures are logged in Directory Insights
TestingTest Webhook shows a simulated request and response
Source IPsListed in JumpCloud's Data Centers article

Common events

JumpCloud doesn't publish a list of event types or an example body. What it does document is the set of fields an alert notification carries:

FieldDescription
alert_idID of the alert
alert_titleTitle of the alert
categoryCategory of the alert
descriptionDetailed description of the alert
di_eventDirectory Insights event associated with the alert
event_descriptionDescription of the event that triggered the alert
priorityCritical, High, Medium or Low
statusOpen, Acknowledged, Resolved or Auto-Resolved
previous_alert_statusThe alert's previous status
rule_idID of the rule that triggered the alert
source_id, source_name, source_type, source_urlThe alert source
initiated_byUser who initiated the change
occurred_at, update_atWhen the alert occurred and was last updated
organization_name, in_console_alertOrganisation name, and the alert as generated in the console

There is no single event type field. Route on rule_id, category, priority or status, and use status with previous_alert_status to tell a new alert from a change to an existing one. JumpCloud doesn't show how these fields are nested or what values category and di_event take, and Access Request notifications aren't described at all, so capture real deliveries before writing strict parsing.

Inspect JumpCloud webhooks as they arrive. Point a JumpCloud channel at a Hookdeck Inspect URL to inspect and replay real deliveries, with no account or setup required. With no example body in the docs, it's the quickest way to see what your rules actually send.

Setting up JumpCloud webhooks

Pick a username and generate a long random password (openssl rand -hex 32 works), and store both in your app's environment.

  1. Log in to the JumpCloud Admin Portal. If your data is stored outside the US, use your region's login URL.
  2. Go to Settings > Notification Channels and click Webhooks.
  3. Click +Add. Leave Webhook Status enabled, and give the channel a unique name of up to 100 characters.
  4. Enter your HTTPS endpoint in URL.
  5. Under authentication, choose Basic Authentication and enter the username and password.
  6. Click Test Webhook to send a simulated request, then Save.

Saving the channel doesn't send anything by itself. Attach it where events come from:

  • For alerts, create or edit an alert rule and add the channel to it.
  • For Directory Insights events, create an Insights rule that uses the channel.
  • For access requests, go to Access > Access Requests and select the events to send to the channel.

Values marked as sensitive, in the URL or in headers, can't be viewed or changed after saving, so keep your own copy.

For local development, use the Hookdeck CLI: hookdeck listen 3000 jumpcloud --path /webhooks/jumpcloud gives you a public HTTPS URL that forwards to your local server, plus a web UI for inspecting and replaying deliveries, with no account required.

With Hookdeck. Create a JumpCloud source in Hookdeck, enter the same Username and Password as the channel, and use the source URL as the channel's URL. JumpCloud doesn't say whether it sends Basic credentials on the first request or only after a 401 challenge. Hookdeck never sends a challenge, so the source only works if JumpCloud sends the Authorization header up front. Send a Test Webhook and check that the request in Hookdeck carries an Authorization: Basic ... header before you rely on it.

Securing JumpCloud webhooks

With Basic Authentication, every request carries Authorization: Basic <base64 of username:password>. Decode it, split on the first colon, and compare both parts against your stored values in constant time. Check lengths before calling crypto.timingSafeEqual, which throws on buffers of different lengths, and evaluate both comparisons so the timing doesn't reveal which one failed.

const crypto = require('crypto');
const express = require('express');

const app = express();

function safeEqual(a, b) {
  const x = Buffer.from(String(a), 'utf8');
  const y = Buffer.from(String(b), 'utf8');
  return x.length === y.length && crypto.timingSafeEqual(x, y);
}

function verifyJumpCloudRequest(authorization) {
  const user = process.env.JUMPCLOUD_WEBHOOK_USERNAME;
  const pass = process.env.JUMPCLOUD_WEBHOOK_PASSWORD;
  if (!user || !pass) return false; // fail closed when unconfigured

  const match = /^basic\s+([A-Za-z0-9+/]+={0,2})$/i.exec((authorization || '').trim());
  if (!match) return false;

  const decoded = Buffer.from(match[1], 'base64').toString('utf8');
  const i = decoded.indexOf(':');
  if (i < 0) return false;

  const userOk = safeEqual(decoded.slice(0, i), user);
  const passOk = safeEqual(decoded.slice(i + 1), pass);
  return userOk && passOk;
}

app.post('/webhooks/jumpcloud', express.raw({ type: '*/*' }), (req, res) => {
  if (!verifyJumpCloudRequest(req.headers.authorization)) {
    return res.sendStatus(401);
  }

  let alert;
  try {
    alert = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }

  res.sendStatus(200);
  // Route on alert.rule_id, alert.priority or alert.status
});

The same check in Python:

import base64
import binascii
import hmac
import os


def verify_jumpcloud_request(authorization) -> bool:
    user = os.environ.get("JUMPCLOUD_WEBHOOK_USERNAME")
    password = os.environ.get("JUMPCLOUD_WEBHOOK_PASSWORD")
    if not user or not password or not authorization:
        return False
    scheme, _, encoded = authorization.strip().partition(" ")
    if scheme.lower() != "basic":
        return False
    try:
        decoded = base64.b64decode(encoded.strip(), validate=True).decode("utf-8")
    except (binascii.Error, UnicodeDecodeError):
        return False
    got_user, sep, got_pass = decoded.partition(":")
    if not sep:
        return False
    user_ok = hmac.compare_digest(got_user.encode(), user.encode())
    pass_ok = hmac.compare_digest(got_pass.encode(), password.encode())
    return user_ok and pass_ok

Basic credentials prove the sender knows the password. They don't protect the body, and nothing stops a captured request being replayed, because there is no signature or timestamp. Base64 is an encoding, not encryption, so the endpoint must be HTTPS. Rotate the password in the channel and your environment if it might have leaked, and consider allowing only the addresses in JumpCloud's Data Centers article at your firewall.

Make JumpCloud webhooks production-ready. Hookdeck Event Gateway checks the channel's Basic credentials on every request, stores each alert durably, and retries delivery to your service well beyond JumpCloud's three attempts.

JumpCloud webhook limitations and pain points

Three retries, then nothing

The Problem: If your endpoint is down for longer than a short backoff window, the alert is lost.

Why It Happens: JumpCloud retries three times with exponential backoff, and only for 5xx, 408 and 429 responses. Other errors, such as a 401 from a misconfigured receiver or a 404 from a wrong path, aren't retried at all. Failures are logged in Directory Insights.

Workarounds:

  • Return 5xx for transient problems in your own code so JumpCloud at least retries.
  • Monitor Directory Insights for webhook failures.
  • For important alerts, check their state in the Admin Portal after an outage.

How Hookdeck Can Help: Hookdeck accepts each notification as soon as JumpCloud sends it, then delivers it to your service with automatic retries on a schedule you control. Issues alert you when deliveries fail, and you can replay any stored alert.

Basic auth must be sent up front

The Problem: A receiver that only checks credentials after issuing a 401 challenge never sees them, if the sender waits for one.

Why It Happens: JumpCloud doesn't document whether its Basic Authentication is preemptive. Hookdeck verifies the request as it arrives and issues no challenge.

Workarounds:

  • Send a Test Webhook and confirm the Authorization header is present on the first request.
  • If it isn't, add a custom secret header instead and check that in your own receiver.

How Hookdeck Can Help: Hookdeck's JumpCloud source checks the Basic credentials on each request. A request without valid credentials still gets a 200 response, but Hookdeck marks it as failed verification and creates no event, so it never reaches your destination. The stored request shows its headers, which makes a missing Authorization header easy to spot.

Thin payload documentation

The Problem: There's no example body, no list of category or di_event values, and no description of the Access Request payload.

Why It Happens: JumpCloud documents a field table for alerts and says payloads follow a fixed JSON schema per event type, without publishing those schemas.

Workarounds:

  • Capture real deliveries for each rule and build your parser from them.
  • Read fields defensively and log bodies you don't recognise instead of rejecting them.

How Hookdeck Can Help: Every request is stored with its body, so you can collect real examples and replay them against your handler. Filters can route alerts by priority or rule_id to different destinations.

Best practices

Use a dedicated credential per channel

Give each channel its own username and password, so you can rotate one without touching the others and tell sources apart in logs.

Treat alerts as state changes

An alert can be sent more than once as its status moves from Open to Acknowledged to Resolved. Key your records on alert_id and update the status rather than creating a new record each time.

Acknowledge fast and process asynchronously

Return 200 once the credentials check out, then open tickets or page people from a background job. See why to process webhooks asynchronously.

Make handlers idempotent

Retries can repeat a notification. Deduplicate on alert_id with status and update_at. See our guide to webhook idempotency.

Conclusion

JumpCloud webhook channels send unsigned JSON alerts, Directory Insights events and access requests, authenticated with Basic credentials you set on the channel. Check those credentials in constant time over HTTPS, confirm they arrive on the first request, and plan for JumpCloud's short retry budget and thin payload documentation.

Hookdeck Event Gateway checks the Basic credentials, stores every alert on arrival and retries delivery to your service, so an outage on your side doesn't mean missed security alerts.

Get started with Hookdeck for free and handle JumpCloud webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.