Skip to content

Change Tracking

Change Tracking gives you a continuous audit trail of what changed on every managed device. The Breeze agent monitors six categories of system changes – software installs and removals, Windows service modifications, startup item additions, network adapter changes, scheduled task updates, and user account mutations – and ships them to the API in compressed batches. Every change is fingerprinted with a SHA-256 hash to prevent duplicates, then stored with before/after snapshots so you can see exactly what was modified.

This feature is designed for compliance auditing, troubleshooting, and drift detection. When a user reports that something stopped working, you can pull up the device’s change timeline and see that a critical service was disabled 20 minutes earlier. When you need to prove to an auditor that no unauthorized software was installed, the change log provides cryptographically deduplicated evidence.


Type Description
software Application installed, removed, or updated
service Windows service or system daemon added, removed, or modified
startup Startup program or launch agent added, removed, or changed
network Network adapter configuration changed (IP, DNS, gateway)
scheduled_task Scheduled task or cron job added, removed, or modified
user_account Local user account created, deleted, or modified
Action Description
added A new item was detected (software installed, user created, etc.)
removed An existing item was deleted (software uninstalled, user removed, etc.)
modified An existing item’s properties changed (service startup type, scheduled task trigger, etc.)
updated A version update was applied (software upgraded, definition file refreshed, etc.)
Field Type Description
id UUID Unique identifier for the change record
deviceId UUID Device where the change was detected
orgId UUID Organization the device belongs to
fingerprint string (64 chars) SHA-256 hash for deduplication
timestamp datetime When the change occurred on the device
changeType enum One of the six change types above
changeAction enum One of the four change actions above
subject string (max 500) Human-readable description of what changed (e.g., “Google Chrome 122.0.6261.69”)
beforeValue JSON State before the change (null for added actions)
afterValue JSON State after the change (null for removed actions)
details JSON Additional context (installer path, registry key, etc.)
createdAt datetime When the record was stored in the database

  1. Agent detects a change. The agent periodically snapshots system state (installed software, services, startup items, etc.) and compares against the previous snapshot. Differences are formatted as change records.

  2. Agent ships changes in a batch. Changes are serialized as JSON, gzip-compressed, and sent to the API via PUT /api/v1/agents/:id/changes. Up to 1,000 changes per request, with a 5 MB compressed body limit and 10 MB decompressed limit.

  3. API deduplicates and stores. Each change is fingerprinted by hashing the combination of timestamp, change type, action, subject, before/after values, and details. A unique constraint on (deviceId, fingerprint) prevents the same change from being inserted twice. Changes are inserted in batches of 200.

  4. Partial success handling. If some changes in a batch fail to insert (e.g., database error on a subset), the API returns HTTP 207 with a partial: true flag, the count of successfully inserted records, and the total submitted.


The changes API supports filtering by device, time range, change type, and action, with cursor-based pagination for efficient iteration through large result sets.

Terminal window
curl "/api/v1/changes?deviceId=DEVICE_UUID&changeType=software&limit=50" \
-H "Authorization: Bearer $TOKEN"
Parameter Type Description
deviceId UUID Filter to a single device
startTime ISO 8601 datetime Only changes at or after this time
endTime ISO 8601 datetime Only changes at or before this time
changeType enum One of: software, service, startup, network, scheduled_task, user_account
changeAction enum One of: added, removed, modified, updated
limit integer Results per page (1 – 500, default 100)
cursor string Opaque cursor for the next page
{
"changes": [
{
"id": "uuid",
"deviceId": "uuid",
"hostname": "DESKTOP-ABC123",
"timestamp": "2026-02-15T14:30:00.000Z",
"changeType": "software",
"changeAction": "added",
"subject": "Visual Studio Code 1.96.2",
"beforeValue": null,
"afterValue": {
"version": "1.96.2",
"vendor": "Microsoft Corporation",
"installLocation": "C:\\Program Files\\Microsoft VS Code"
},
"details": null
}
],
"total": 1847,
"showing": 50,
"hasMore": true,
"nextCursor": "eyJ0aW1lc3RhbXAiOiIyMDI2..."
}

The API uses cursor-based pagination for stable results when new changes arrive during iteration. The nextCursor field is a base64url-encoded JSON object containing the timestamp and ID of the last returned record. Pass it as the cursor query parameter to get the next page.

Terminal window
# First page
curl "/api/v1/changes?deviceId=DEVICE_UUID&limit=100" \
-H "Authorization: Bearer $TOKEN"
# Next page (using nextCursor from the previous response)
curl "/api/v1/changes?deviceId=DEVICE_UUID&limit=100&cursor=eyJ0aW1lc3RhbXAi..." \
-H "Authorization: Bearer $TOKEN"

The agent pushes changes directly to the API using bearer token authentication.

Terminal window
PUT /api/v1/agents/:agentId/changes
Content-Type: application/json
Content-Encoding: gzip
Authorization: Bearer AGENT_TOKEN
{
"changes": [
{
"timestamp": "2026-02-15T14:30:00.000Z",
"changeType": "software",
"changeAction": "added",
"subject": "Visual Studio Code 1.96.2",
"afterValue": {
"version": "1.96.2",
"vendor": "Microsoft Corporation"
}
}
]
}
Status Meaning
200 All changes accepted (or empty batch)
207 Partial success – some changes inserted, some failed
400 Invalid request body or decompression failure
404 Agent/device not found
413 Request body exceeds 5 MB compressed or 10 MB decompressed
500 All changes failed to insert

Every change is assigned a SHA-256 fingerprint computed from a stable concatenation of:

  1. Canonical ISO 8601 timestamp
  2. Change type
  3. Change action
  4. Subject
  5. Stable-sorted JSON of beforeValue
  6. Stable-sorted JSON of afterValue
  7. Stable-sorted JSON of details

This fingerprint is stored in a VARCHAR(64) column with a unique index on (deviceId, fingerprint). When the agent ships the same change twice (e.g., after a retry), the database ON CONFLICT DO NOTHING clause silently skips the duplicate.

Within a single request, the API also deduplicates by fingerprint before attempting insertion, so batch payloads with repeated entries are handled efficiently.


Method Path Description
GET /api/v1/changes List changes with filters and cursor pagination
PUT /api/v1/agents/:id/changes Agent ingest endpoint for shipping change batches

Changes not appearing for a device Verify the agent is enrolled and the device exists in the platform. The ingest endpoint looks up the device by agentId, not by device UUID. If the agent is not enrolled, the endpoint returns 404.

Duplicate changes appearing This should not happen due to fingerprint deduplication. If you see exact duplicates, check whether the agent is sending changes with different timestamps for the same event (e.g., using local time vs. UTC). The fingerprint includes the timestamp, so a one-second difference produces a different hash.

Large change volumes after a software deployment This is expected. Mass deployments (e.g., pushing a new application to 500 devices) will generate 500 software:added change records. Use the changeType and changeAction filters to focus on specific categories, and cursor pagination to iterate efficiently.

207 partial success responses A 207 means some changes in the batch were inserted and some failed. This can happen if the database has transient issues. The agent should not retry the entire batch because the successfully inserted changes would be deduplicated anyway. Check server logs for the underlying database error.

Request body too large (413) The ingest endpoint limits compressed payloads to 5 MB and decompressed payloads to 10 MB. If agents are hitting this limit, reduce the batch size or increase the shipping frequency. These limits can be tuned with the CHANGE_INGEST_MAX_BODY_BYTES and CHANGE_INGEST_MAX_DECOMPRESSED_BYTES environment variables.