Skip to content

Device Backup

Device Backup provides centralized backup management for devices enrolled in Breeze. You define storage configurations (where backups go), policies (what gets backed up, on what schedule, and how long to keep it), then monitor everything through the backup dashboard. Backups produce snapshots that can be browsed file-by-file and restored to the original device or a different target. All backup operations are scoped to an organization and enforced through the standard authentication middleware.

The backup system is organized around five core concepts: configurations, policies, jobs, snapshots, and restore operations. Configurations define the storage target. Policies bind a configuration to devices with a schedule and retention rules. Jobs represent individual backup or restore executions. Snapshots are the point-in-time archives produced by completed backup jobs. Restore operations recover data from a snapshot back to a device.


Key Concepts

Storage Providers

Backup configurations support two storage provider types:

ProviderDescription
s3Amazon S3 or S3-compatible object storage. Configuration details include bucket, region, prefix, storage class, and encryption settings.
localLocal filesystem or network-attached storage. Configuration details include the mount path, retention days, and compression algorithm.

The database schema defines additional provider types (azure_blob, google_cloud, backblaze) for future use, but the current API validates against s3 and local.

Backup Types (Schema)

The database schema defines four backup type classifications:

TypeDescription
fileFile-level backup of selected directories and files
system_imageFull system image capture
databaseDatabase dump or snapshot
applicationApplication-specific backup (e.g., email, configuration)

Job Status Lifecycle

Backup and restore jobs move through the following statuses:

StatusMeaning
queuedJob created and waiting to be dispatched to the agent
runningBackup or restore is actively in progress on the device
completedJob finished successfully; a snapshot was created (for backups)
failedJob encountered an error; check the error field for details
canceledJob was explicitly canceled by a user before completion

Job Triggers

Each backup job records how it was initiated:

TriggerDescription
scheduledTriggered automatically by a backup policy schedule
manualTriggered on-demand via POST /backup/jobs/run/:deviceId
restoreCreated as a side-effect of initiating a restore operation

Retention Rules

Policies define a retention strategy with three tiers:

FieldDefaultDescription
keepDaily7Number of daily snapshots to retain
keepWeekly4Number of weekly snapshots to retain
keepMonthly3Number of monthly snapshots to retain

Snapshots that fall outside the retention window are eligible for cleanup.


Backup Configurations

Backup configurations define the storage target where backup data is written. Each configuration is scoped to an organization and includes a provider type, a name, enable/disable state, and a freeform details object for provider-specific settings.

Creating a Configuration

Terminal window
POST /backup/configs
Content-Type: application/json
Authorization: Bearer <token>
{
"name": "Primary S3",
"provider": "s3",
"enabled": true,
"details": {
"bucket": "breeze-backups",
"region": "us-east-1",
"prefix": "org-001",
"storageClass": "STANDARD_IA",
"encryption": "AES256"
}
}
FieldTypeRequiredDescription
namestringYesHuman-readable name (minimum 1 character)
providerstringYesStorage provider: s3 or local
enabledbooleanNoWhether the config is active. Defaults to true
detailsobjectNoProvider-specific settings (bucket, path, encryption, etc.)

The response returns the full configuration object with a generated id, createdAt, and updatedAt timestamp.

Updating a Configuration

Terminal window
PATCH /backup/configs/:id
Content-Type: application/json
{
"name": "Primary S3 - Updated",
"details": {
"storageClass": "GLACIER"
}
}

Only the fields you include are updated. The details object is shallow-merged with the existing details rather than replaced entirely, so you can update individual keys without losing others.

Testing Connectivity

Validate that the storage target is reachable:

Terminal window
POST /backup/configs/:id/test

The response includes the provider, a status field (returns "success" on a successful test), and a checkedAt timestamp. The configuration’s lastTestedAt field is also updated.

{
"id": "cfg-s3-primary",
"provider": "s3",
"status": "success",
"checkedAt": "2026-02-18T12:00:00.000Z"
}

Deleting a Configuration

Terminal window
DELETE /backup/configs/:id

Returns { "deleted": true } on success.

Database Schema

The backup_configs table stores the persistent configuration records with these columns:

ColumnTypeDescription
idUUIDPrimary key, auto-generated
org_idUUIDForeign key to organizations
namevarchar(200)Configuration name
typeenumBackup type: file, system_image, database, application
providerenumStorage provider: local, s3, azure_blob, google_cloud, backblaze
provider_configJSONBProvider-specific configuration
scheduleJSONBOptional schedule data
retentionJSONBOptional retention rules
compressionbooleanCompression enabled (default true)
encryptionbooleanEncryption enabled (default true)
encryption_keytextEncryption key (when encryption is enabled)
is_activebooleanWhether the configuration is active (default true)
created_attimestampCreation timestamp

Indexes exist on org_id, type, provider, and is_active for efficient filtering.


Backup Policies

Backup policies are templates that bind a backup configuration to a set of target devices, sites, or groups with a defined schedule and retention strategy.

Creating a Policy

Terminal window
POST /backup/policies
Content-Type: application/json
Authorization: Bearer <token>
{
"name": "Daily Endpoints",
"configId": "cfg-s3-primary",
"enabled": true,
"targets": {
"deviceIds": ["dev-001", "dev-002", "dev-003"],
"siteIds": ["site-nyc"],
"groupIds": []
},
"schedule": {
"frequency": "daily",
"time": "02:00",
"timezone": "UTC"
},
"retention": {
"keepDaily": 7,
"keepWeekly": 4,
"keepMonthly": 3
}
}
FieldTypeRequiredDescription
namestringYesPolicy name (minimum 1 character)
configIdstringYesID of the backup configuration to use. Must belong to the same organization
enabledbooleanNoWhether the policy is active. Defaults to true
targetsobjectNoTarget assignments (see below)
scheduleobjectYesSchedule definition (see below)
retentionobjectNoRetention rules. Defaults: 7 daily, 4 weekly, 3 monthly

Target Assignment

Policies can target devices at three levels of granularity:

FieldTypeDescription
deviceIdsstring[]Specific device UUIDs to back up
siteIdsstring[]All devices at these sites
groupIdsstring[]All devices in these device groups

Schedule Configuration

FieldTypeRequiredDescription
frequencystringYesdaily, weekly, or monthly
timestringYesTime of day in HH:MM format (24-hour)
timezonestringNoIANA timezone. Defaults to UTC
dayOfWeekintegerNoDay of week for weekly schedules (0 = Sunday through 6 = Saturday)
dayOfMonthintegerNoDay of month for monthly schedules (1-28)

Updating a Policy

Terminal window
PATCH /backup/policies/:id
Content-Type: application/json
{
"schedule": {
"frequency": "weekly",
"dayOfWeek": 0
},
"retention": {
"keepWeekly": 8
}
}

All fields are optional. Nested objects (targets, schedule, retention) are partially merged — only the sub-fields you include are updated; others retain their current values. When changing configId, the new configuration must exist within the same organization.

Deleting a Policy

Terminal window
DELETE /backup/policies/:id

Returns { "deleted": true } on success.

Database Schema

The backup_policies table links configurations to targets:

ColumnTypeDescription
idUUIDPrimary key, auto-generated
config_idUUIDForeign key to backup_configs
target_typevarchar(50)Type of target (e.g., device, site, group)
target_idUUIDID of the target entity
includesJSONBPaths or items to include in the backup
excludesJSONBPaths or items to exclude from the backup
priorityintegerExecution priority (default 50)

Indexes exist on config_id and the composite (target_type, target_id).


Backup Jobs

Jobs represent individual backup or restore executions. They are created automatically by scheduled policies or manually via the API.

Running a Manual Backup

Trigger an immediate backup for a specific device:

Terminal window
POST /backup/jobs/run/:deviceId
Authorization: Bearer <token>

The system resolves the backup configuration by:

  1. Finding a policy that targets the given deviceId within the same organization.

  2. If a matching policy exists, using that policy’s configId.

  3. If no matching policy exists, falling back to any backup configuration within the organization.

If no configuration is available, the endpoint returns 400 with "No backup config available".

The created job has:

  • type: "backup"
  • trigger: "manual"
  • status: "running"
  • policyId: the matched policy ID, or null if resolved via fallback

Listing Jobs

Terminal window
GET /backup/jobs
ParameterTypeDescription
statusstringFilter by status: queued, running, completed, failed, canceled
deviceId or devicestringFilter by device UUID
datestringFilter by date prefix (e.g., 2026-02-18)
fromstringStart of date range (ISO 8601)
tostringEnd of date range (ISO 8601)

Results are sorted by start time (or creation time) in descending order (most recent first).

Getting Job Details

Terminal window
GET /backup/jobs/:id

Returns the full job object including sizeBytes, error, snapshotId (if completed), and all timestamps.

Canceling a Job

Terminal window
POST /backup/jobs/:id/cancel

Only jobs with status running or queued can be canceled. The job’s status is set to canceled, completedAt is set to the current time, and the error field is set to "Canceled by user".

Database Schema

The backup_jobs table tracks all backup and restore executions:

ColumnTypeDescription
idUUIDPrimary key, auto-generated
config_idUUIDForeign key to backup_configs
device_idUUIDForeign key to devices
statusenumpending, running, completed, failed, cancelled, partial
typeenumscheduled, manual, incremental
started_attimestampWhen execution began
completed_attimestampWhen execution finished
total_sizebigintTotal data size in bytes
transferred_sizebigintBytes actually transferred
file_countintegerNumber of files processed
error_countintegerNumber of errors encountered
error_logtextDetailed error messages
snapshot_idvarchar(200)Reference to the resulting snapshot

Indexes exist on config_id, device_id, status, and started_at.


Snapshots

Snapshots are point-in-time archives created by completed backup jobs. Each snapshot records the device it came from, the configuration used, the total size, file count, a human-readable label, and the storage location.

Listing Snapshots

Terminal window
GET /backup/snapshots
ParameterTypeDescription
deviceIdstringFilter snapshots by device UUID
configIdstringFilter snapshots by backup configuration UUID

Results are sorted by creation time in descending order (newest first).

Getting Snapshot Details

Terminal window
GET /backup/snapshots/:id

Returns the full snapshot object:

{
"id": "snap-001",
"deviceId": "dev-001",
"configId": "cfg-s3-primary",
"jobId": "job-001",
"createdAt": "2026-02-18T00:10:00.000Z",
"sizeBytes": 321987654,
"fileCount": 45678,
"label": "Daily 2025-02-14",
"location": "s3://breeze-backups/org-001/dev-001/2025-02-14"
}

Browsing Snapshot Contents

View the file and directory tree stored within a snapshot:

Terminal window
GET /backup/snapshots/:id/browse

The response returns a hierarchical tree structure:

{
"snapshotId": "snap-001",
"data": [
{
"name": "/",
"path": "/",
"type": "directory",
"children": [
{
"name": "Users",
"path": "/Users",
"type": "directory",
"children": [
{
"name": "alice",
"path": "/Users/alice",
"type": "directory",
"children": [
{
"name": "Documents",
"path": "/Users/alice/Documents",
"type": "directory",
"children": [
{
"name": "report.pdf",
"path": "/Users/alice/Documents/report.pdf",
"type": "file",
"sizeBytes": 245760,
"modifiedAt": "2026-02-16T00:00:00.000Z"
}
]
}
]
}
]
}
]
}
]
}

Each item in the tree has a type of either file or directory. Files include sizeBytes and modifiedAt. Directories contain a children array with nested items.

Database Schema

The backup_snapshots table stores metadata for each snapshot:

ColumnTypeDescription
idUUIDPrimary key, auto-generated
job_idUUIDForeign key to backup_jobs
device_idUUIDForeign key to devices
snapshot_idvarchar(200)External snapshot identifier
timestamptimestampWhen the snapshot was taken
sizebigintTotal snapshot size in bytes
file_countintegerNumber of files in the snapshot
is_incrementalbooleanWhether this is an incremental snapshot (default false)
parent_snapshot_idUUIDSelf-referencing foreign key for incremental chain
expires_attimestampWhen the snapshot is eligible for deletion
metadataJSONBAdditional snapshot metadata

Indexes exist on job_id, device_id, snapshot_id, and parent_snapshot_id.


Restore Operations

Restore operations recover data from a snapshot back to a device. The restore can target the original device or a different device within the same organization.

Initiating a Restore

Terminal window
POST /backup/restore
Content-Type: application/json
Authorization: Bearer <token>
{
"snapshotId": "snap-001",
"deviceId": "dev-001",
"targetPath": "/var/restore"
}
FieldTypeRequiredDescription
snapshotIdstringYesID of the snapshot to restore from
deviceIdstringNoTarget device for the restore. Defaults to the snapshot’s original device
targetPathstringNoFilesystem path on the target device where data should be restored

The restore operation creates two records:

  1. A restore job (tracked in the restore jobs collection) with a progress field (0-100).
  2. A backup job entry with type: "restore" and trigger: "restore", so restore operations appear in the unified job list alongside regular backups.

The restore job is created with status queued and progress: 0.

Checking Restore Status

Terminal window
GET /backup/restore/:id

Returns the restore job with current progress:

{
"id": "restore-001",
"snapshotId": "snap-002",
"deviceId": "dev-002",
"status": "running",
"targetPath": "/var/restore",
"createdAt": "2026-02-18T00:00:00.000Z",
"startedAt": "2026-02-18T00:10:00.000Z",
"completedAt": null,
"updatedAt": "2026-02-18T00:25:00.000Z",
"progress": 45
}

Database Schema

The restore_jobs table tracks restore operations:

ColumnTypeDescription
idUUIDPrimary key, auto-generated
snapshot_idUUIDForeign key to backup_snapshots
device_idUUIDForeign key to devices (target device)
restore_typeenumfull, selective, or bare_metal
target_pathtextFilesystem path for the restored data
selected_pathsJSONBArray of specific paths for selective restore
statusenumUses the same backup_status enum as jobs
started_attimestampWhen the restore began
completed_attimestampWhen the restore finished
restored_sizebigintBytes restored so far
restored_filesintegerNumber of files restored
initiated_byUUIDForeign key to users who started the restore

Indexes exist on snapshot_id, device_id, and status.


Backup Dashboard

The dashboard endpoints provide aggregated metrics, compliance data, per-device backup status, and storage usage history.

Organization Dashboard

Terminal window
GET /backup/dashboard
Authorization: Bearer <token>

Returns a summary of the organization’s backup health:

{
"data": {
"totals": {
"configs": 2,
"policies": 2,
"jobs": 7,
"snapshots": 3
},
"jobsLast24h": {
"completed": 1,
"failed": 0,
"running": 1,
"queued": 0
},
"storage": {
"totalBytes": 1568375087,
"snapshots": 3
},
"coverage": {
"protectedDevices": 4
},
"latestJobs": [ ... ]
}
}
SectionFieldsDescription
totalsconfigs, policies, jobs, snapshotsTotal count of each resource type
jobsLast24hcompleted, failed, running, queuedJob status breakdown for the last 24 hours
storagetotalBytes, snapshotsCumulative storage used across all snapshots
coverageprotectedDevicesNumber of unique devices assigned to at least one backup policy
latestJobs(array)The 5 most recent jobs, sorted by start time descending

Per-Device Backup Status

Check the backup status for a specific device:

Terminal window
GET /backup/status/:deviceId
Authorization: Bearer <token>
{
"data": {
"deviceId": "dev-001",
"protected": true,
"policyId": "pol-daily-endpoints",
"lastJob": { ... },
"lastSuccessAt": "2026-02-18T00:10:00.000Z",
"lastFailureAt": null,
"nextScheduledAt": "2026-02-19T02:00:00.000Z"
}
}
FieldDescription
protectedWhether the device is assigned to any backup policy
policyIdID of the matching policy, or null if unprotected
lastJobThe most recent job for this device (any status)
lastSuccessAtTimestamp of the last completed backup
lastFailureAtTimestamp of the last failed backup
nextScheduledAtComputed next run time based on the policy schedule. null if the device has no policy

The nextScheduledAt calculation accounts for the policy’s frequency, time, dayOfWeek (for weekly), and dayOfMonth (for monthly). If the next scheduled time has already passed today, it advances to the next applicable day.

Storage Usage History

Track storage growth over time with the usage history endpoint:

Terminal window
GET /backup/usage-history?days=14
Authorization: Bearer <token>
ParameterTypeDefaultDescription
daysinteger14Number of days to include (3-90)

The response provides a time series of cumulative storage by provider:

{
"data": {
"days": 14,
"start": "2026-02-04T00:00:00.000Z",
"end": "2026-02-18T00:00:00.000Z",
"providers": ["s3", "local"],
"points": [
{
"timestamp": "2026-02-04T00:00:00.000Z",
"totalBytes": 0,
"providers": [
{ "provider": "s3", "bytes": 0 },
{ "provider": "local", "bytes": 0 }
]
},
{
"timestamp": "2026-02-05T00:00:00.000Z",
"totalBytes": 321987654,
"providers": [
{ "provider": "s3", "bytes": 321987654 },
{ "provider": "local", "bytes": 0 }
]
}
]
}
}

Each point in the points array represents one day and includes the cumulative total bytes and a breakdown by storage provider. The running totals accumulate day over day, so the final point reflects the total storage used across all snapshots within the time window.


Audit Logging

All mutating backup operations generate audit log entries. The following actions are recorded:

ActionTrigger
backup.config.createNew backup configuration created
backup.config.updateConfiguration updated (logs changed fields)
backup.config.deleteConfiguration deleted
backup.config.testConnectivity test executed
backup.policy.createNew backup policy created
backup.policy.updatePolicy updated (logs changed fields)
backup.policy.deletePolicy deleted
backup.job.runManual backup triggered
backup.job.cancelJob canceled
backup.restore.createRestore operation initiated

Each audit entry includes the orgId, resourceType, resourceId, and contextual details such as the provider, device ID, or list of changed fields.


Multi-Tenant Scoping

All backup endpoints are protected by the authMiddleware and scoped to the caller’s organization. The resolveScopedOrgId helper resolves the organization ID from the authentication context:

  • Organization-scoped tokens: The orgId is taken directly from the token.
  • Partner-scoped tokens: If the partner has access to exactly one organization, that organization is used. Otherwise the request returns 400 with "orgId is required for this scope".
  • System-scoped tokens: Must include an orgId in the token payload.

Every read and write operation verifies that the requested resource belongs to the resolved organization before returning data or making changes. Resources from other organizations are not visible and return 404.


API Reference

Backup Configurations

MethodPathDescription
GET/backup/configsList all configurations for the organization
POST/backup/configsCreate a new backup configuration
GET/backup/configs/:idGet a configuration by ID
PATCH/backup/configs/:idUpdate configuration fields
DELETE/backup/configs/:idDelete a configuration
POST/backup/configs/:id/testTest storage connectivity

Backup Policies

MethodPathDescription
GET/backup/policiesList all policies for the organization
POST/backup/policiesCreate a new backup policy
PATCH/backup/policies/:idUpdate policy fields
DELETE/backup/policies/:idDelete a policy

Backup Jobs

MethodPathDescription
GET/backup/jobsList jobs with optional filters (status, deviceId, date, from, to)
GET/backup/jobs/:idGet job details by ID
POST/backup/jobs/run/:deviceIdTrigger a manual backup for a device
POST/backup/jobs/:id/cancelCancel a running or queued job

Snapshots

MethodPathDescription
GET/backup/snapshotsList snapshots (?deviceId=, ?configId=)
GET/backup/snapshots/:idGet snapshot metadata
GET/backup/snapshots/:id/browseBrowse the snapshot’s file tree

Restore Operations

MethodPathDescription
POST/backup/restoreInitiate a restore from a snapshot
GET/backup/restore/:idCheck restore job status and progress

Dashboard

MethodPathDescription
GET/backup/dashboardOrganization backup summary and metrics
GET/backup/status/:deviceIdPer-device backup status and next scheduled run
GET/backup/usage-historyStorage usage time series (?days=14)

Troubleshooting

Manual backup returns “No backup config available”. This means no backup configuration exists for your organization, or no policy targets the specified device and there are no fallback configurations. Create a backup configuration first, then either create a policy targeting the device or ensure at least one configuration exists in the organization.

Job stays in queued status indefinitely. The backup job was created but has not been picked up by an agent. Verify that the target device is online and that the Breeze agent is running. Check that Redis and BullMQ are operational if your deployment uses background job processing.

Cancel request returns 409 Conflict. Only jobs with status running or queued can be canceled. If the job has already completed, failed, or been canceled, it cannot be canceled again. Check the current job status with GET /backup/jobs/:id.

Snapshot browse returns an empty data array. The snapshot exists but its file tree contents are not available. This can happen if the backup agent did not report file-level metadata, or if the snapshot data has been pruned from the browsing index while the snapshot record remains.

Restore targets the wrong device. When calling POST /backup/restore, the deviceId field is optional. If omitted, the restore targets the snapshot’s original device. To restore to a different device, explicitly set deviceId to the target device’s UUID. Both devices must belong to the same organization.

Dashboard shows 0 protected devices. The protectedDevices count is derived from the deviceIds arrays in your backup policies. Devices targeted only via siteIds or groupIds are resolved at policy execution time and may not be reflected in this count. Ensure your policies explicitly list device IDs for accurate coverage reporting.

Usage history shows unexpected storage totals. The usage history endpoint calculates cumulative storage from snapshot sizeBytes values, broken down by the provider of each snapshot’s associated backup configuration. If snapshots were deleted or retention cleanup removed old snapshots, the historical totals may decrease. The days parameter (3-90, default 14) controls how far back the time series extends.

Partner-scoped token returns “orgId is required for this scope”. Partner tokens that have access to multiple organizations cannot automatically resolve which organization to use. The partner must have access to exactly one organization, or the request must include the orgId in the authentication context.