SafeGuard

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/backups

The 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:

CodeHTTP StatusDescription
rest_not_logged_in401No authentication provided
rest_forbidden403Insufficient permissions
backup_not_found404Backup ID does not exist
storage_not_configured400Specified storage provider is not configured
backup_in_progress409A backup is already running
license_inactive403License is not active

Pagination

List endpoints support pagination via query parameters:

ParameterTypeDefaultDescription
per_pageinteger20Number of items per page (max: 100)
pageinteger1Page 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: 3

Example:

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/backups

Query 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/backups

Request body:

{
  "type": "full",
  "storage": "s3",
  "label": "Pre-deploy backup"
}
FieldTypeRequiredDescription
typestringNofull, files, database, or incremental. Default: full
storagestringNoStorage provider slug. Default: primary provider
labelstringNoHuman-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/restore

Request body:

{
  "backup_id": "bkp_a1b2c3d4",
  "components": ["files", "database"]
}
FieldTypeRequiredDescription
backup_idstringYesThe ID of the backup to restore
componentsarrayNoArray 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-providers

Response:

[
  {
    "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}/test


Schedule Endpoints

List schedules

GET /wp-json/safeguard/v1/schedules

Response:

[
  {
    "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/schedules

Request body:

{
  "type": "incremental",
  "frequency": "daily",
  "storage": "s3",
  "time": "02:00",
  "retention": 30
}
FieldTypeRequiredDescription
typestringYesfull, files, database, or incremental
frequencystringYes1hour, 2hours, 4hours, 8hours, 12hours, daily, weekly, or monthly
storagestringYesStorage provider slug
timestringNoPreferred start time in HH:MM 24-hour format. Default: 02:00
retentionintegerNoNumber 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/create

Request body:

{
  "mode": "quick",
  "staging_roles": {
    "3": "full",
    "7": "developer"
  }
}
FieldTypeRequiredDescription
modestringNoquick or full. Default: quick
staging_rolesRecord<number, string>NoPer-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}/settings

Request body:

{
  "staging_roles": {
    "3": "full",
    "7": "developer",
    "12": "full"
  }
}
FieldTypeRequiredDescription
staging_rolesRecord<number, string>NoUpdated 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-job

Returns 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-job

Returns 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"
  }
}
FieldTypeDescription
chain_next.actionstringThe follow-up action that was triggered (e.g., staging_create, migrate)
chain_next.job_idstringThe 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

EventTrigger
backup.completedA backup finishes successfully
backup.failedA backup fails
restore.completedA restore finishes successfully
restore.failedA 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...

On this page