Real-time
Delivered within seconds of the event happening — no polling delay.
Signed
Every payload carries an HMAC signature you can verify before trusting it.
Retried automatically
A failed delivery is retried on a backoff schedule, not dropped silently.
The envelope
Every webhook delivery is a POST request with the same outer shape, regardless of which event fired:Setting up a webhook
1
Create the webhook
POST /api/v1/webhooks with a URL and the event types you want to subscribe to:secret — this is the only time you’ll ever see it. Store it securely; there’s no “reveal secret” option later, only delete and recreate.2
Verify your endpoint can receive it
Trigger a matching event (or wait for a real one) and confirm a request actually arrives at your URL, with the headers described below.
3
Verify the signature
Before trusting the payload, recompute the signature yourself and compare it against the
X-AssetGullak-Signature header — see Verifying signatures.Webhooks are available on the Enterprise plan. Creating a webhook on a plan that doesn’t include this feature returns a 403.
Verifying signatures
Every delivery includes two headers:
The signature is computed over the raw request body — the exact bytes sent, before any JSON parsing — using your webhook’s secret as the HMAC key. Recompute it the same way on your end and compare.
Retries and delivery behavior
If your endpoint doesn’t respond with a 2xx status — or doesn’t respond at all within 10 seconds — the delivery is retried automatically:
After the fifth attempt, the delivery is marked permanently failed and isn’t retried further. Your endpoint returning a 2xx at any point stops the retry sequence for that specific delivery.
Each delivery attempt is logged and viewable via
GET /api/v1/webhooks/{webhook_id}/deliveries — useful for diagnosing a missed event without needing to reproduce it live.
Event reference
Devices
Devices
device.online / device.offlinedevice.warningSame shape as above, plus current resource usage:device.deletedAssets
Assets
asset.createdasset.deletedasset.status_changedasset.reassignedprevious_assignee / new_assignee are polymorphic — either can be an employee, department, or location. previous_assignee is null for a genuinely first-time assignment; new_assignee is null when an asset is unassigned entirely. expected_return_date is set only for temporary assignments — null for a normal, permanent one, and always null when the event represents an unassignment.asset.device_linked / asset.device_unlinkedasset.verifiedFired when IT staff directly marks an asset as physically verified — a standalone action distinct from the Physical Verification confirmation cycle (which is employee self-attestation via email). This event represents someone from IT personally confirming the asset in person.Commands
Commands
command.completed / command.failedSame shape for both — exit_code carries the finer-grained success/failure signal. Full stdout/stderr aren’t included; fetch those via the API if you need them.command.expiredNo exit_code — the command never ran.command_batch.completedOne summary event per batch, not one event per device.Bulk import
Bulk import
bulk_import.completedPolicy violations
Policy violations
subject is polymorphic — a violation can be about a device or an asset.policy_violation.opened / policy_violation.resolvedpolicy_violation.acknowledgedSame as above, plus who acknowledged it:policy_violation.unacknowledgedSame shape as policy_violation.opened — acknowledged_by is no longer relevant once cleared.policy_violation.assignedpolicy_violation.unassignedSame shape as policy_violation.opened — no assigned_to field, since there’s no assignee left to describe.policy_violation.fix_attemptedFired when someone claims they’ve addressed a violation — this is not the same as resolution. Resolution is always verified automatically by the policy engine on its next evaluation pass, never claimed directly by a person; this event just marks that someone believes they’ve fixed it and the system should double check. If the claim turns out to be right, a policy_violation.resolved event follows once the policy engine confirms it; if not, the violation simply stays open.Managing webhooks
A company can have up to 10 webhooks active at once. Creating or managing webhooks requires an Owner or Admin role.
Webhook URLs must resolve to a genuine public address. URLs pointing at
localhost, private IP ranges, or other internal/reserved addresses are rejected at creation and update time.Related
- Locations —
assigned_locationandlast_seen_locationin device event payloads both reference locations - Location signals — how
last_seen_locationgets populated automatically