Snapshots API

Snapshots provide full database backup and restore capabilities. Manual snapshots can be created on demand, and automatic backups can be scheduled with configurable retention policies. Restore is a destructive operation that replaces the current database and requires a server restart.

Quick Start

Create a snapshot before a risky operation, list available snapshots, and restore if needed:

// 1. Create a manual snapshot before a major change
interface Snapshot {
id: string;
label: string;
sizeBytes: number;
createdAt: string;
}

const snapshot: Snapshot = await fetch('/api/snapshots', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ label: 'Before schema migration' }),
}).then((r: Response) => r.json());

console.log(`Snapshot ${snapshot.id} created (${(snapshot.sizeBytes / 1024).toFixed(0)} KB)`);

// 2. List all snapshots with disk usage stats
const { snapshots, totalBytes, snapshotCount }: {
snapshots: Snapshot[];
totalBytes: number;
snapshotCount: number;
} = await fetch('/api/snapshots').then((r: Response) => r.json());
console.log(`${snapshotCount} snapshots using ${(totalBytes / 1024 / 1024).toFixed(1)} MB`);

// 3. If something goes wrong, restore from the snapshot
const restore: {
success: boolean;
requiresRestart: boolean;
preRestoreSnapshotId: string;
} = await fetch(`/api/snapshots/${snapshot.id}/restore`, {
method: 'POST',
}).then((r: Response) => r.json());

if (restore.requiresRestart) {
console.log(`Restored! Pre-restore backup: ${restore.preRestoreSnapshotId}`);
console.log('Restart the server to load the restored database');
}

// 4. Enable automatic daily backups with retention policy
await fetch('/api/snapshots/settings', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  enabled: true,
  interval: '1d',
  maxCount: 10,
  maxAgeWeeks: 4,
}),
});

Base URL

/api/snapshots

Endpoints

List Snapshots

GET /api/snapshots

List all snapshots with aggregate disk usage statistics.

Response Body

FieldTypeReqDescription
snapshotsobject[]*Array of snapshot records
totalBytesnumber*Total disk usage of all snapshots in bytes
snapshotCountnumber*Number of snapshots on disk

List all snapshots to see what backups are available and how much disk they use:

// List snapshots with disk usage
interface SnapshotList {
snapshots: { label: string; sizeBytes: number; createdAt: string }[];
totalBytes: number;
snapshotCount: number;
}

const { snapshots, totalBytes, snapshotCount }: SnapshotList =
await fetch('/api/snapshots').then((r: Response) => r.json());

console.log(`${snapshotCount} snapshots, ${(totalBytes / 1024 / 1024).toFixed(1)} MB total`);
snapshots.forEach((s) => {
console.log(`  ${s.label} — ${(s.sizeBytes / 1024).toFixed(0)} KB — ${s.createdAt}`);
});

Example response:

{
  "snapshots": [
    {
      "id": "snap-2a3b-4c5d",
      "label": "Before schema migration",
      "type": "manual",
      "sizeBytes": 1482752,
      "createdAt": "2026-04-03T10:00:00.000Z"
    },
    {
      "id": "snap-6e7f-8g9h",
      "label": "Auto backup",
      "type": "auto",
      "sizeBytes": 1478400,
      "createdAt": "2026-04-02T00:00:00.000Z"
    }
  ],
  "totalBytes": 2961152,
  "snapshotCount": 2
}

Create Snapshot

POST /api/snapshots

Create a manual database snapshot. Fails with 409 if another snapshot operation is in progress.

Request Body

FieldTypeReqDescription
labelstringSnapshot label (defaults to "Manual snapshot")

Response 201 Created — Snapshot record

Create a labeled snapshot before risky operations so you can rollback if needed:

// Create a manual snapshot before risky changes
interface Snapshot {
id: string;
label: string;
sizeBytes: number;
}

const snapshot: Snapshot = await fetch('/api/snapshots', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ label: 'Before schema migration' }),
}).then((r: Response) => r.json());

console.log(`Snapshot ${snapshot.id}: ${snapshot.label}`);
console.log(`Size: ${(snapshot.sizeBytes / 1024).toFixed(0)} KB`);

Example response:

{
  "id": "snap-2a3b-4c5d",
  "label": "Before schema migration",
  "type": "manual",
  "sizeBytes": 1482752,
  "createdAt": "2026-04-03T10:00:00.000Z"
}

Errors: 409 — Another snapshot operation in progress, 500 — Creation failure

Get Snapshot

GET /api/snapshots/{id}

Retrieve details of a single snapshot.

Response 200 — Snapshot record

const snapshot: { label: string; sizeBytes: number; createdAt: string } =
await fetch('/api/snapshots/snap-2a3b-4c5d').then((r: Response) => r.json());
console.log(`${snapshot.label} — ${(snapshot.sizeBytes / 1024).toFixed(0)} KB — ${snapshot.createdAt}`);

Errors: 404 — Snapshot not found

Delete Snapshot

DELETE /api/snapshots/{id}

Permanently delete a snapshot and its backup files from disk.

Response 200{ "success": true }

// Delete an old snapshot to reclaim disk space
await fetch('/api/snapshots/snap-2a3b-4c5d', { method: 'DELETE' });

Errors: 404 — Snapshot not found

Restore from Snapshot

POST /api/snapshots/{id}/restore

Restore the database from a snapshot. This is a destructive operation that replaces the current database. A pre-restore snapshot is automatically created for safety. Fails if any tasks are currently running.

Response Body

FieldTypeReqDescription
successboolean*Whether the restore completed
requiresRestartboolean*Whether a server restart is needed
preRestoreSnapshotIdstring*ID of the auto-created pre-restore backup
messagestring*Instruction to restart the server

Restore from a snapshot — a pre-restore backup is automatically created so you can undo the restore if needed:

// Restore the database from a snapshot
interface RestoreResult {
success: boolean;
requiresRestart: boolean;
preRestoreSnapshotId: string;
message: string;
}

const result: RestoreResult = await fetch('/api/snapshots/snap-2a3b-4c5d/restore', {
method: 'POST',
}).then((r: Response) => r.json());

if (result.success) {
console.log(`Restored! Pre-restore backup: ${result.preRestoreSnapshotId}`);
if (result.requiresRestart) {
  console.log('Restart the server to load the restored database');
}
} else {
console.error('Restore failed');
}

Example response:

{
  "success": true,
  "requiresRestart": true,
  "preRestoreSnapshotId": "snap-8h9i-0j1k",
  "message": "Database restored. Restart the server to load the restored database."
}

Errors: 409 — Another snapshot operation in progress or tasks are running, 500 — Restore failure


Snapshot Settings

Get Settings

GET /api/snapshots/settings

Read auto-backup and retention configuration.

Response Body

FieldTypeReqDescription
enabledstring*Auto-backup enabled ("true" or "false", default: "false")
intervalstring*Backup interval (e.g., "1d", "12h", default: "1d")
maxCountstring*Maximum snapshots to retain (default: "10")
maxAgeWeeksstring*Maximum age before pruning (default: "4")

Check the current auto-backup and retention configuration:

// Check auto-backup configuration
interface SnapshotSettings {
enabled: string;
interval: string;
maxCount: string;
maxAgeWeeks: string;
}

const settings: SnapshotSettings = await fetch('/api/snapshots/settings')
.then((r: Response) => r.json());

console.log(`Auto-backup: ${settings.enabled === 'true' ? 'ON' : 'OFF'}`);
console.log(`Interval: ${settings.interval}`);
console.log(`Retention: ${settings.maxCount} snapshots, max ${settings.maxAgeWeeks} weeks`);

Example response:

{
  "enabled": "true",
  "interval": "1d",
  "maxCount": "10",
  "maxAgeWeeks": "4"
}

Update Settings

PUT /api/snapshots/settings

Update auto-backup and retention settings. All fields are optional — only provided fields are updated.

Request Body (all optional)

FieldTypeReqDescription
enabledboolean | stringToggle auto-backup
intervalstringBackup interval (e.g., "1d", "6h")
maxCountnumber | stringMaximum snapshots to retain
maxAgeWeeksnumber | stringMaximum snapshot age in weeks

Response 200{ "success": true }

Enable automatic backups with a 12-hour interval and retain up to 20 snapshots:

// Enable auto-backup with custom retention
await fetch('/api/snapshots/settings', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  enabled: true,
  interval: '12h',
  maxCount: 20,
  maxAgeWeeks: 8,
}),
});