Skip to main content

Proposal: Collection Control

Status

Proposed

Overview

Collection Control is the API surface for managing data collection at three levels: client, device, and user. It introduces a three-tier precedence model (Client > Device > User), a collection_mode on clients, collection_state + log_shipping_enabled on devices, and a status on users. All control changes are recorded in a unified audit log.

This proposal covers:

  • A new Collection Control domain with an audit log endpoint
  • A new PATCH /clients/{client_id} endpoint for changing a client's collection mode
  • New collection_mode field on the Client DTO
  • New status field on UserRef (and by extension User and DeviceUserRef)
  • New status filter and sort options on GET /users
  • Updated PATCH /users/{user_id} accepting a status field with transition rules
  • New bulk user control endpoints: archive, activate, delete
  • Updated UserSummary with total, archived_users, and deleted_users counts
  • New collection_state, log_shipping_enabled, and effective_mode fields on the Device DTO
  • New collection_state and effective_mode filter and sort options on GET /devices
  • New PATCH /devices/{device_id} endpoint for changing device-level controls
  • New bulk device control endpoints: enable, disable, delete

Embedding Decisions

EntityFieldEmbedded on Ref?Reason
User statusUserRefYesBounded (3 values), always needed for display (archived badge), stored directly on users table — no join required
Device collection_stateDeviceRefNoOperational control metadata, not identity/reference data
Device log_shipping_enabledDeviceRefNoSame as above
Device effective_modeDeviceRefNoRequires JOIN to clients table — violates DeviceRef's "no joins" rule
effective_mode computationDeviceYes (computed)Client > Device precedence; requires JOIN on full Device queries only

DTOs

Client (updated)

Added collection_mode field.

FieldTypeNullableDescription
collection_modestringNoAgent data collection mode: discovery, saas_usage, disabled, deleted. Default: saas_usage.

DB source: clients.collection_mode

UserRef (updated)

Added status field.

FieldTypeNullableDescription
statusstringNoUser collection status: active, archived, deleted. Default: active.

DB source: users.status

UserSummary (updated)

Expanded with status-aware counts.

FieldTypeDescription
totalnumberTotal user count across all statuses
active_usersnumberUsers with status = 'active' whose last activity is within the stale threshold
stale_usersnumberUsers with status = 'active' whose last activity is older than the threshold, or who have never reported activity
stale_thresholdnumberThe threshold in seconds used for the calculation
archived_usersnumberUsers with status = 'archived'
deleted_usersnumberUsers with status = 'deleted'

BulkUserControlResult (new)

Response shape for all bulk user control endpoints.

FieldTypeDescription
succeededstring (uuid)[]User IDs successfully updated
failedobject[]User IDs that failed with error details
failed[].idstring (uuid)The user ID that failed
failed[].errorstringReason for the failure

Device (updated)

Added three collection control fields.

FieldTypeNullableDescription
collection_statestringNoDevice-level collection state: enabled, disabled, deleted. Default: enabled.
log_shipping_enabledbooleanNoWhether full log shipping is enabled for this device. Default: false.
effective_modestringNoComputed effective collection mode after applying Client > Device precedence: discovery, saas_usage, disabled, deleted.

DB source: devices.collection_state, devices.log_shipping_enabled, computed from clients.collection_mode + devices.collection_state

Effective Mode Computation

Client collection_modeDevice collection_stateeffective_mode
disabled or deleted(any)client mode (overrides device)
discovery or saas_usagedisabled or deleteddevice state
discovery or saas_usageenabledclient mode

BulkDeviceControlResult (new)

Response shape for all bulk device control endpoints.

FieldTypeDescription
succeededstring (uuid)[]Device IDs successfully updated
failedobject[]Device IDs that failed with error details
failed[].idstring (uuid)The device ID that failed
failed[].errorstringReason for the failure

CollectionControlAuditEntry (new)

FieldTypeNullableDescription
idstring (uuid)NoUnique audit entry ID
entity_typestringNoType of entity affected: client, device, user
entity_idstring (uuid)NoID of the affected entity
actor_idstring (uuid)NoID of the user who made the change
actionstringNoType of change (e.g. collection_mode_changed, collection_state_changed, status_changed)
previous_valueobjectYesPrevious state as a JSON object
new_valueobjectYesNew state as a JSON object
created_atstring (ISO 8601)NoWhen the change was made

Endpoints

PATCH /v2/clients/{client_id}

Update client-level settings. Supports changing collection_mode. Returns the updated Client object.

Request body: { collection_mode?: string }

Transition rules: discovery, saas_usage, disabled are freely interchangeable. Transition to deleted is irreversible.

GET /v2/clients/{client_id}/collection-control/audit-log

Returns a paginated audit trail of all collection control changes for a client.

Query params: entity_type, entity_id, sort_order, page_size, cursor

Response: { data: CollectionControlAuditEntry[], total_count, next_cursor }

GET /v2/clients/{client_id}/users (updated)

Added status query parameter (default: active, supports comma-separated) and status as a sort_by option.

PATCH /v2/clients/{client_id}/users/{user_id} (updated)

Added status field to request body with transition rules:

  • activearchived (reversible)
  • activedeleted (irreversible)
  • archivedactive (reversible)
  • archiveddeleted (irreversible)
  • deleted → any (rejected, 400)

POST /v2/clients/{client_id}/users/archive

Bulk archive users. Returns BulkUserControlResult.

POST /v2/clients/{client_id}/users/activate

Bulk reactivate archived users. Returns BulkUserControlResult.

POST /v2/clients/{client_id}/users/delete

Bulk soft-delete users (irreversible). Returns BulkUserControlResult.

GET /v2/clients/{client_id}/devices (updated)

Added collection_state and effective_mode query parameters. Added collection_state and effective_mode as sort_by options.

PATCH /v2/clients/{client_id}/devices/{device_id}

Update a single device's collection controls. Supports collection_state and log_shipping_enabled. deleted is irreversible. Returns the updated Device object.

POST /v2/clients/{client_id}/devices/enable

Bulk enable device collection. Optionally set log_shipping_enabled alongside. Returns BulkDeviceControlResult.

POST /v2/clients/{client_id}/devices/disable

Bulk disable device collection. Optionally set log_shipping_enabled alongside. Returns BulkDeviceControlResult.

POST /v2/clients/{client_id}/devices/delete

Bulk delete devices (irreversible, triggers agent self-uninstall). Returns BulkDeviceControlResult.

Open Questions

None.

Guideline References

  • UserRef embedding rulestatus satisfies all three conditions: bounded, always needed for display, no join required.
  • DeviceRef embedding rulecollection_state and effective_mode are excluded from DeviceRef because they are operational metadata and effective_mode requires a JOIN.
  • Bulk action pattern — partial success model: per-entity atomicity, succeeded/failed response shape, scales to 10,000 entities.
  • Irreversibility patterndeleted transitions are one-way for both users and devices; collection_mode=deleted on a client is also irreversible.

Checklist