API Reference
SafeGuard REST API endpoints for programmatic backup management.
SafeGuard exposes REST API endpoints under the /wp-json/safeguard/v1/ namespace. All endpoints return JSON responses and follow WordPress REST API conventions.
Authentication
All API endpoints require authentication. The authenticating user must have the manage_options capability (Administrator role).
SafeGuard supports two authentication methods:
WordPress application passwords (available since WordPress 5.6) are the recommended method for external API access. Generate one from Users → Your Profile → Application Passwords in the WordPress admin.
curl -u "admin:XXXX XXXX XXXX XXXX XXXX XXXX" \
https://example.com/wp-json/safeguard/v1/backupsThe application password is space-separated in groups of four characters.
Authentication Errors
If authentication fails, SafeGuard returns:
{
"code": "rest_not_logged_in",
"message": "You are not currently logged in.",
"data": {
"status": 401
}
}If the user lacks sufficient permissions:
{
"code": "rest_forbidden",
"message": "You do not have permission to manage backups.",
"data": {
"status": 403
}
}Error Response Format
All error responses follow a consistent structure. Your integration should check the code field for programmatic handling and display the message field to users. The data.status field mirrors the HTTP status code.
{
"code": "error_code_string",
"message": "A human-readable description of the error.",
"data": {
"status": 400
}
}Common error codes:
| Code | HTTP Status | Description |
|---|---|---|
rest_not_logged_in | 401 | No authentication provided |
rest_forbidden | 403 | Insufficient permissions |
backup_not_found | 404 | Backup ID does not exist |
storage_not_configured | 400 | Specified storage provider is not configured |
backup_in_progress | 409 | A backup is already running |
license_inactive | 403 | License is not active |
Pagination
List endpoints support pagination via query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
per_page | integer | 20 | Number of items per page (max: 100) |
page | integer | 1 | Page number |
Pagination metadata is returned in the response headers X-WP-Total (total items) and X-WP-TotalPages (total pages). Use these to build pagination controls in your UI.
X-WP-Total: 47
X-WP-TotalPages: 3Example:
curl -u "admin:XXXX XXXX XXXX XXXX XXXX XXXX" \
"https://example.com/wp-json/safeguard/v1/backups?per_page=10&page=2"Backup Endpoints
List backups
GET /wp-json/safeguard/v1/backupsQuery parameters: per_page, page, type (filter by backup type), storage (filter by provider slug)
Response:
[
{
"id": "bkp_a1b2c3d4",
"type": "full",
"status": "completed",
"size": 245000000,
"storage": "s3",
"label": "Pre-deploy backup",
"components": ["files", "database"],
"created_at": "2026-03-01T02:00:05Z",
"completed_at": "2026-03-01T02:00:49Z",
"duration": 44
},
{
"id": "bkp_e5f6g7h8",
"type": "incremental",
"status": "completed",
"size": 12000000,
"storage": "s3",
"label": null,
"components": ["files", "database"],
"created_at": "2026-02-28T02:00:03Z",
"completed_at": "2026-02-28T02:00:15Z",
"duration": 12
}
]Create a backup
POST /wp-json/safeguard/v1/backupsRequest body:
{
"type": "full",
"storage": "s3",
"label": "Pre-deploy backup"
}| Field | Type | Required | Description |
|---|---|---|---|
type | string | No | full, files, database, or incremental. Default: full |
storage | string | No | Storage provider slug. Default: primary provider |
label | string | No | Human-readable label |
Response (202 Accepted):
{
"id": "bkp_m3n4o5p6",
"type": "full",
"status": "running",
"storage": "s3",
"label": "Pre-deploy backup",
"created_at": "2026-03-02T14:30:00Z"
}Get backup details
GET /wp-json/safeguard/v1/backups/{id}Response:
{
"id": "bkp_a1b2c3d4",
"type": "full",
"status": "completed",
"size": 245000000,
"storage": "s3",
"label": "Pre-deploy backup",
"components": ["files", "database"],
"file_count": 12847,
"table_count": 42,
"created_at": "2026-03-01T02:00:05Z",
"completed_at": "2026-03-01T02:00:49Z",
"duration": 44,
"parent_id": null
}Delete a backup
DELETE /wp-json/safeguard/v1/backups/{id}This permanently deletes the backup record and removes the backup files from remote storage. This action cannot be undone.
Response:
{
"deleted": true,
"id": "bkp_a1b2c3d4"
}Restore a backup
POST /wp-json/safeguard/v1/restoreRequest body:
{
"backup_id": "bkp_a1b2c3d4",
"components": ["files", "database"]
}| Field | Type | Required | Description |
|---|---|---|---|
backup_id | string | Yes | The ID of the backup to restore |
components | array | No | Array of "files" and/or "database". Default: both |
Response (202 Accepted):
{
"id": "bkp_a1b2c3d4",
"status": "restoring",
"components": ["files", "database"],
"started_at": "2026-03-02T15:00:00Z"
}Storage Endpoints
List storage providers
GET /wp-json/safeguard/v1/storage-providersResponse:
[
{
"slug": "s3",
"provider": "Amazon S3",
"bucket": "my-backups",
"region": "us-east-1",
"path": "safeguard/",
"is_primary": true,
"last_tested": "2026-03-01T12:00:00Z",
"test_status": "passed"
},
{
"slug": "dropbox",
"provider": "Dropbox",
"path": "/Apps/SafeGuard",
"is_primary": false,
"last_tested": "2026-02-28T09:15:00Z",
"test_status": "passed"
}
]Test connection
POST /wp-json/safeguard/v1/storage-providers/{id}/testSchedule Endpoints
List schedules
GET /wp-json/safeguard/v1/schedulesResponse:
[
{
"id": 1,
"type": "incremental",
"frequency": "daily",
"storage": "s3",
"time": "02:00",
"retention": 30,
"next_run": "2026-03-03T02:00:00Z",
"last_run": "2026-03-02T02:00:05Z",
"enabled": true
}
]Create a schedule
POST /wp-json/safeguard/v1/schedulesRequest body:
{
"type": "incremental",
"frequency": "daily",
"storage": "s3",
"time": "02:00",
"retention": 30
}| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | full, files, database, or incremental |
frequency | string | Yes | 1hour, 2hours, 4hours, 8hours, 12hours, daily, weekly, or monthly |
storage | string | Yes | Storage provider slug |
time | string | No | Preferred start time in HH:MM 24-hour format. Default: 02:00 |
retention | integer | No | Number of backups to keep before rotating. Default: 10 |
Response (201 Created):
{
"id": 3,
"type": "incremental",
"frequency": "daily",
"storage": "s3",
"time": "02:00",
"retention": 30,
"next_run": "2026-03-03T02:00:00Z",
"enabled": true
}Delete a schedule
DELETE /wp-json/safeguard/v1/schedules/{id}Response:
{
"deleted": true,
"id": 1
}Staging Endpoints
Create a staging site
POST /wp-json/safeguard/v1/staging/createRequest body:
{
"mode": "quick",
"staging_roles": {
"3": "full",
"7": "developer"
}
}| Field | Type | Required | Description |
|---|---|---|---|
mode | string | No | quick or full. Default: quick |
staging_roles | Record<number, string> | No | Per-user staging role assignment. Keys are WordPress user IDs, values are full or developer |
Response (202 Accepted):
{
"id": "stg_x1y2z3w4",
"mode": "quick",
"status": "creating",
"staging_roles": {
"3": "full",
"7": "developer"
},
"created_at": "2026-03-20T10:00:00Z"
}Update staging settings
POST /wp-json/safeguard/v1/staging/{id}/settingsRequest body:
{
"staging_roles": {
"3": "full",
"7": "developer",
"12": "full"
}
}| Field | Type | Required | Description |
|---|---|---|---|
staging_roles | Record<number, string> | No | Updated per-user staging role assignment. Keys are WordPress user IDs, values are full or developer |
Response:
{
"id": "stg_x1y2z3w4",
"staging_roles": {
"3": "full",
"7": "developer",
"12": "full"
},
"updated_at": "2026-03-20T11:30:00Z"
}Get active staging job
GET /wp-json/safeguard/v1/staging/active-jobReturns the currently active staging job, if any. This is useful for reconnecting to an in-progress job after a page refresh.
Response (200 OK):
{
"id": "stg_x1y2z3w4",
"mode": "quick",
"status": "creating",
"progress": 45,
"created_at": "2026-03-20T10:00:00Z"
}If no staging job is active, the endpoint returns:
{
"active": false
}Migration Endpoints
Get active migration job
GET /wp-json/safeguard/v1/migrate/active-jobReturns the currently active migration job, if any. This is useful for reconnecting to an in-progress migration after a page refresh.
Response (200 OK):
{
"id": "mig_a1b2c3d4",
"status": "running",
"progress": 62,
"source_url": "https://old-site.com",
"created_at": "2026-03-20T10:00:00Z"
}If no migration job is active, the endpoint returns:
{
"active": false
}Job Chaining
When a backup job completes, the result may include a chain_next field that triggers an automatic follow-up job. This is used internally to chain dependent operations (e.g., a staging creation that first requires a backup).
Job chaining is automatic and does not require any client-side action. If you are polling a backup job and it completes with a chain_next field, the follow-up job has already been enqueued. The chain_next object tells you what job was triggered so you can track it.
Example backup completion response with chain_next:
{
"id": "bkp_a1b2c3d4",
"type": "full",
"status": "completed",
"size": 245000000,
"duration": 44,
"created_at": "2026-03-20T10:00:00Z",
"completed_at": "2026-03-20T10:00:44Z",
"chain_next": {
"action": "staging_create",
"job_id": "stg_x1y2z3w4"
}
}| Field | Type | Description |
|---|---|---|
chain_next.action | string | The follow-up action that was triggered (e.g., staging_create, migrate) |
chain_next.job_id | string | The ID of the follow-up job that was enqueued |
Webhooks
SafeGuard can send webhook notifications to an external URL when backup events occur. Configure webhooks under SafeGuard → Settings → Webhooks or via the API.
Webhook Events
| Event | Trigger |
|---|---|
backup.completed | A backup finishes successfully |
backup.failed | A backup fails |
restore.completed | A restore finishes successfully |
restore.failed | A restore fails |
Webhook Payload
All webhook payloads are sent as POST requests with a JSON body:
{
"event": "backup.completed",
"timestamp": "2026-03-02T02:00:49Z",
"site_url": "https://example.com",
"data": {
"id": "bkp_a1b2c3d4",
"type": "full",
"status": "completed",
"size": 245000000,
"storage": "s3",
"duration": 44,
"created_at": "2026-03-02T02:00:05Z",
"completed_at": "2026-03-02T02:00:49Z"
}
}For backup.failed events, the data object includes an error field:
{
"event": "backup.failed",
"timestamp": "2026-03-02T02:01:30Z",
"site_url": "https://example.com",
"data": {
"id": "bkp_q7r8s9t0",
"type": "full",
"status": "failed",
"storage": "s3",
"error": "Storage upload failed: connection timeout after 30 seconds.",
"created_at": "2026-03-02T02:00:05Z",
"failed_at": "2026-03-02T02:01:30Z"
}
}Webhook Security
Each webhook request includes an X-SafeGuard-Signature header containing an HMAC-SHA256 signature of the request body, signed with your webhook secret. Always verify this signature on your server to confirm the request is authentic.
X-SafeGuard-Signature: sha256=a1b2c3d4e5f6...