Guide to 1Password Device Trust (Kolide) Webhooks: Features and Best Practices
1Password Device Trust, formerly Kolide, checks that laptops meet your security policy before their users can sign in through SSO. It runs checks on each device, raises issues such as a disabled firewall or an outdated OS, and blocks sign-in until the user fixes them or is granted an exemption. Its webhooks push those device, issue, sign-in and admin events to your own systems, so a ticketing tool, CMDB or SIEM can stay current without polling the API.
Here you'll find the 13 Device Trust events, how to verify a signature that arrives in the Authorization header, what the endpoint identifier header is for, and how to work around an undocumented delivery policy.
What are 1Password Device Trust (Kolide) webhooks?
Admins with Full Access register webhook endpoints in the Device Trust admin console and pick which events each one receives. Every delivery is a JSON POST with the same four top-level fields: event, id, timestamp and data. Each endpoint has its own signing secret and its own static identifier.
The product still lives on kolide.com domains, and payload URLs point at api.kolide.com. Delivery logs for each endpoint are kept for six months in the console.
1Password Device Trust (Kolide) webhook features
| Feature | Details |
|---|---|
| Signature | hex HMAC-SHA256 |
| Signature header | authorization |
| Body | JSON |
| Event routing | Body field event |
Also from the Device Trust webhook documentation:
X-Kolide-Webhook-Identifieris a static value per endpoint, shown next to the secret.- Rotating the secret also changes the identifier.
- Delivery logs are kept for six months.
- Retries, timeouts, accepted status codes and source IPs are not documented.
Events and payloads
| Event routing | Body field event |
These are the 13 documented event types. Handle the ones you need and log the rest.
This is the admin_users.created sample from the webhook samples library:
{
"event": "admin_users.created",
"id": "01HDW1V4TDE1B3KCAMR8V7Z4S5",
"timestamp": "2024-06-21T21:28:17Z",
"data": {
"user_id": 1,
"name": "Fred Flintstone",
"email": "fred@theflintstones.com",
"access": "Admin",
"admin_user_url": "https://api.kolide.com/admin_users/1"
}
}
data changes shape with each event. device_ carries device_id, device_name, device_status and device_url. issues.new and issues.resolved carry the issue, the device and the check that raised it, and title can be null on a new issue. The test event's data is just { "message": "This is a test webhook event" }. Most objects include a URL on api.kolide.com if you need the full record.
Setting up 1Password Device Trust (Kolide) webhooks
- In the admin console, go to Settings > Developers > Webhooks and add a webhook. You need Full Access.
- Enter your endpoint URL, for example
https://example.com/webhooks/kolide. - Subscribe to the events you'll handle, such as
device_,trust.status_ changed issues.new,issues.resolvedandauth_logs.failure. - Open the endpoint and choose Reveal Secret. Store the secret as
KOLIDE_WEBHOOK_SECRET, used as plain text. - Send the test event and check that your endpoint verifies it.
For Hookdeck, create a source of the 1Password Device Trust (Kolide) type with the revealed secret as its verification secret, and register the source URL in step 2. To work locally:
npx hookdeck-cli listen 3000 kolide --path /webhooks/kolide
Securing 1Password Device Trust (Kolide) webhooks
The Authorization header holds a bare hex HMAC-SHA256 of the raw body, keyed with the endpoint's secret. It isn't a bearer token: compare the whole value as one digest and don't strip a scheme. See verifying webhook authenticity.
Node.js verification
const crypto = require('node:crypto');
function verifySignature(rawBody, headers, secret) {
if (!Buffer.isBuffer(rawBody) || typeof secret !== 'string' || !secret) return false;
const signature = headers['authorization'];
if (typeof signature !== 'string') return false;
const key = Buffer.from(secret, 'utf8');
const message = rawBody;
const candidates = [signature];
const expected = crypto.createHmac('sha256', key).update(message).digest();
return candidates.some((candidate) => {
if (!/^[0-9a-fA-F]{64}$/.test(candidate)) return false;
const received = Buffer.from(candidate, 'hex');
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
});
}
X-Kolide-Webhook-Identifier can screen traffic at a web application firewall, but anyone who sees one request learns it, so the HMAC check above is what proves authenticity.
This handler stores the event by id and acts on blocked devices from a worker:
const express = require('express');
const app = express();
app.post('/webhooks/kolide', express.raw({ type: '*/*' }), async (req, res) => {
const secret = process.env.KOLIDE_WEBHOOK_SECRET;
if (!secret) return res.sendStatus(500);
if (!verifySignature(req.body, req.headers, secret)) return res.sendStatus(401);
let parsed;
try {
parsed = JSON.parse(req.body.toString('utf8'));
} catch {
return res.sendStatus(400);
}
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return res.sendStatus(400);
const { event, id, timestamp, data } = parsed;
try {
if (event === 'webhook.test') return res.sendStatus(200);
const isNew = await storeDeviceTrustEvent(id, event, timestamp, data);
if (isNew && event === 'device_trust.status_changed' && data.device_status === 'blocked') {
await queueBlockedDeviceAlert(data.device_id, data.device_name);
}
} catch {
return res.sendStatus(503);
}
res.sendStatus(200);
});
storeDeviceTrustEvent returns false for an id it already holds, so a repeated delivery doesn't raise a second alert.
The timestamp field isn't signed, so don't build a freshness check from it. Hookdeck's Device Trust source verifies the same Authorization HMAC without a time limit, responds 200, and records a mismatch as failed verification.
Make 1Password Device Trust (Kolide) webhooks production-ready. Hookdeck Event Gateway verifies each Device Trust event, keeps a full history alongside the console's delivery log, and retries delivery to your SIEM or ticketing system on a schedule you set.
1Password Device Trust (Kolide) webhook limitations and pain points
Retry behaviour is undocumented
The Problem: You can't tell whether a missed device_ or auth_logs.failure will be sent again.
Why It Happens: The documentation covers events, headers and signing, but not retries, timeouts or which status codes count as success.
Workarounds:
- Return
200quickly and do the work afterwards. - After an outage, check the endpoint's delivery log in the console and reconcile devices and issues through the API.
How Hookdeck Can Help: Hookdeck accepts each event as it arrives and manages delivery to your service with retries you configure, so Device Trust's own retry behaviour matters less.
The signature travels in Authorization
The Problem: Infrastructure in front of your receiver can drop, rewrite or redact the signature.
Why It Happens: Device Trust puts the HMAC in the standard Authorization header, which proxies, API gateways and logging tools often treat as a credential.
Workarounds:
- Make sure every hop forwards
Authorizationunchanged to the code that verifies it. - Don't let application middleware try to parse it as a bearer token.
How Hookdeck Can Help: Hookdeck verifies the signature as the request arrives and records every header, Authorization included, so you can see exactly what Device Trust sent before any of your own infrastructure touched it.
Secret rotation changes the identifier
The Problem: Rotating the secret breaks a WAF rule that checks the identifier, as well as signature verification.
Why It Happens: The secret and X-Kolide-Webhook-Identifier change together, and there's no documented overlap period.
Workarounds:
- Update the secret and any identifier rule in the same change.
- Or create a second endpoint with the new secret, switch over, then delete the old one.
How Hookdeck Can Help: If Device Trust points at a Hookdeck source, only the source needs the new secret, and your WAF can allow Hookdeck's traffic instead.
Payloads are thin for some events
The Problem: An event tells you something happened without all the detail you need, and title can be null.
Why It Happens: Several events carry IDs and a URL rather than the full object.
Workarounds:
- Fetch the device, issue or auth log from the API in a worker.
- Fall back to the check name when an issue has no title.
How Hookdeck Can Help: Hookdeck transformations can reshape events for each destination, for example mapping check.name into a title field your ticketing system expects.
Best practices
Treat Authorization as data
Read it, verify it, and keep it out of logs, but never pass it to an authentication layer.
Deduplicate on id
Every event has an id. Store it with a unique constraint as the idempotency guide describes, especially for events that open tickets or page someone.
Decouple alerts from receipt
Creating a ticket or posting to chat belongs after the response. Asynchronous webhook processing explains why.
Use separate endpoints for separate consumers
A SIEM wants audit_log.recorded and auth_logs.*, while a helpdesk wants issues.* and requests.*. Separate endpoints keep each consumer's subscriptions and secret independent.
Conclusion
1Password Device Trust webhooks report device trust, issue, sign-in and admin activity, signed with a hex HMAC-SHA256 of the body in the Authorization header. Verify the raw bytes, preserve that header through every proxy, deduplicate on id, and don't count on retries, because Device Trust doesn't document them.
Hookdeck Event Gateway gives Device Trust events durable storage and retries you can see.
Get started with Hookdeck for free and handle 1Password Device Trust (Kolide) webhooks reliably in minutes.