# 1Password Device Trust (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)

```javascript
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)

```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/](https://github.com/hookdeck/webhook-skills/tree/main/skills/kolide-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/kolide-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/kolide-webhooks/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](https://github.com/hookdeck/webhook-skills/blob/main/skills/kolide-webhooks/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.

| Event | Triggered When |
| --- | --- |
| `devices.created` | A device is created |
| `devices.registered` | A device is registered to an owner |
| `devices.destroyed` | A device is deleted |
| `device_trust.status_changed` | A device's trust status changes (for example to `blocked`) |
| `issues.new` | A check finds a new issue on a device |
| `issues.resolved` | An issue is resolved |
| `auth_logs.success` / `auth_logs.failure` | A device-trust authentication succeeds or fails |
| `audit_log.recorded` | An admin action is recorded in the audit log |
| `admin_users.created` | An admin user is created |
| `requests.registration` / `requests.issue_exemption` | An end user asks to register a device or for an issue exemption |
| `webhook.test` | You send the test event from the admin console |

See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/kolide-webhooks/references/overview.md) for payload shapes.

## Important Headers

| Header | Description |
| --- | --- |
| `Authorization` | Bare hex HMAC-SHA256 of the raw body, no `Bearer` or other prefix |
| `X-Kolide-Webhook-Identifier` | Identifies the webhook endpoint; changes when the secret is rotated |

## Environment Variables

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

```

## Local Development

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

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/kolide-webhooks/references/overview.md) - Kolide webhook concepts, events, payloads
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/kolide-webhooks/references/setup.md) - Registering the endpoint, the signing secret, Hookdeck setup
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/kolide-webhooks/references/verification.md) - Signature details and debugging