Agent skill

1Password Device Trust (Kolide) Webhooks Skill

Receive and verify 1Password Device Trust (Kolide) webhooks. Use when setting up Kolide webhook handlers, debugging Authorization header signature verification, or handling device and compliance events such as devices.created, issues.new, device_trust.status_changed, and auth_logs.failure.

Install this skill

npx skills add hookdeck/webhook-skills --skill kolide-webhooks


When to Use This Skill

  • How do I receive 1Password Device Trust (Kolide) webhooks?
  • How do I verify the Kolide webhook signature in the Authorization header?
  • How do I handle devices.created, issues.new, device_trust.status_changed, or auth_logs.failure events?
  • Why does my Kolide webhook signature verification fail, or why does Bearer parsing break on it?
  • How do I send the Kolide webhook.test event to my endpoint?

Essential Code (USE THIS)

Every delivery is an HTTP POST with Authorization: <hex>. The value is the bare lowercase hex HMAC-SHA256 of the raw request body, keyed with the endpoint's signing secret (used as-is, as UTF-8 bytes). There is no Bearer scheme, no prefix, and no timestamp; only the body is signed, so there is no replay window to check. Despite its name, the header is not a credential to look up: it is the signature.

Verification (Node.js)

const crypto = require('crypto');

// rawBody: Buffer of the exact request bytes (express.raw({ type: '*/*' }), not express.json)
function verifyKolide(rawBody, authorization, secret) {
  if (!secret || typeof authorization !== 'string') return false;
  const received = authorization.trim();
  // 64 hex chars first: Buffer.from(x, 'hex') silently drops bad characters
  if (!/^[0-9a-f]{64}$/i.test(received)) return false;
  const expected = crypto.createHmac('sha256', secret) // secret as UTF-8, not decoded
    .update(rawBody).digest();
  return crypto.timingSafeEqual(Buffer.from(received, 'hex'), expected);
}

Verification (Python)

import hashlib, hmac, re

def verify_kolide(raw_body: bytes, authorization: str | None, secret: str) -> bool:
    if not secret or authorization is None:
        return False
    received = authorization.strip()
    if not re.fullmatch(r"[0-9a-fA-F]{64}", received):  # exact length and alphabet
        return False
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    # bytes on both sides: a non-ASCII header gives a rejection, not a 500
    return hmac.compare_digest(received.lower().encode(), expected.encode())

A missing, empty, malformed, or mismatching Authorization value gets a 401.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Setup with Hookdeck

Hookdeck's KOLIDE source type (labelled "1Password Device Trust (Kolide)") verifies this signature: HMAC, SHA-256, header authorization, hex encoding. Paste the endpoint's signing secret into the source and register the Hookdeck source URL as the endpoint URL. See references/setup.md.

Event Envelope and Types

Every event is { event, id, timestamp, data }. Branch on the top-level event field and dedupe on id. Delivery retries, timeout, and accepted status codes are not documented, so return 200 quickly and keep handlers idempotent.

EventTriggered When
devices.createdA device is created
devices.registeredA device is registered to an owner
devices.destroyedA device is deleted
device_trust.status_changedA device's trust status changes (for example to blocked)
issues.newA check finds a new issue on a device
issues.resolvedAn issue is resolved
auth_logs.success / auth_logs.failureA device-trust authentication succeeds or fails
audit_log.recordedAn admin action is recorded in the audit log
admin_users.createdAn admin user is created
requests.registration / requests.issue_exemptionAn end user asks to register a device or for an issue exemption
webhook.testYou send the test event from the admin console

See references/overview.md for payload shapes.

Important Headers

HeaderDescription
AuthorizationBare hex HMAC-SHA256 of the raw body, no Bearer or other prefix
X-Kolide-Webhook-IdentifierIdentifies the webhook endpoint; changes when the secret is rotated

Environment Variables

KOLIDE_WEBHOOK_SECRET=your_endpoint_signing_secret   # "Reveal Secret" on the webhook settings screen

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 kolide --path /webhooks/kolide

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 12, 2026

View on GitHub →