Backup REST API
The /backup endpoints control the backups of Core Hub to Gluesync Connect. Use them to turn backups on, start one, change the settings or turn them off from a script.
The endpoints follow the conventions of the Core Hub REST API: JSON over HTTP, the root of the server as base path, and a Bearer token from /authentication/login. The Control Plane uses the same endpoints.
Access
Every /backup endpoint needs a user with the Super Admin role. A backup holds every credential of the installation, and the recovery key that opens it is chosen here. Other roles get 403 Forbidden.
curl -X GET https://your-gluesync.com/backup \
-H "Authorization: Bearer <your-token>"
Endpoints
| Method | Path | Description |
|---|---|---|
|
|
The backup status, the plan limits and the backups Gluesync Connect holds for this site, and the warnings about the installation. |
|
|
Turn backups on. The body carries the public recovery key. See Enable backups. |
|
|
Seal the next backups to a new recovery key. The body carries the new public key. See Rotate the recovery key. |
|
|
Turn backups off and forget the public key. Backups already in Gluesync Connect are kept until their retention ends. |
|
|
Change the settings. Only |
|
|
Start a backup now. It obeys the frequency of the plan. |
There is no setting for the frequency. It is the interval of the plan: one backup per day on the free plan, one per hour on a PRO plan.
Generate a recovery key and its public key
The recovery key is a private key. The best key is one that never reaches Core Hub. You generate the key pair outside Core Hub, keep the private half and send only the public half. The Control Plane does this in the browser. For a script, use the --generate-recovery-key command of the Core Hub program. It starts no server, makes no network call and reads no Core Hub data.
java -jar <path to the Core Hub jar> --generate-recovery-key
With Docker, from any host that has the Core Hub image:
docker run --rm <Core Hub image> ./entrypoint.sh --generate-recovery-key
The command prints, in this order:
-
the recovery key,
GSRK1-XXXXX-XXXXX-…. It is secret. Store it as described in Keep the recovery key; -
the public key, the base64 text of the 32 raw bytes of the X25519 public key. It is not secret. This is the value you send to the API as
publicKey; -
the key ID, 16 hexadecimal characters. Core Hub and Gluesync Connect use it to name the key. It reveals nothing about the key.
The exit status is 0. An argument that the command does not know gives exit status 1 and a usage message.
|
If you lose the recovery key, the backups sealed to it cannot be opened. Nobody can recover it, including MOLO17. |
Enable backups
Send the public key:
curl -X POST https://your-gluesync.com/backup/enable \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{"publicKey": "<base64 public key>"}'
| Field | Type | Description |
|---|---|---|
|
string, optional |
The public key from Generate a recovery key and its public key: base64 of the 32 raw bytes of an X25519 public key. The body is limited to 4096 bytes. |
The answer is 200 OK with the ID of the key. Core Hub starts the first backup shortly after.
{ "keyId": "3fa9c01d5b7e2a64" }
Core Hub refuses a key that is not base64, that is not 32 bytes long, or that is a point of small order, with 400 Bad Request, the code invalid_request and "field": "publicKey". A field other than publicKey, a body that is not a JSON object, and a publicKey that is not a string give 400 too.
409 Conflict with the code backup_already_enabled means backups are already on. Use /backup/rotate-key to change the key.
Let Core Hub generate the key
Without a publicKey (an empty body or {}), Core Hub generates the key itself and answers with it:
{
"recoveryKey": "GSRK1-XXXXX-XXXXX-...",
"keyId": "3fa9c01d5b7e2a64"
}
The answer carries Cache-Control: no-store. The key is shown once and Core Hub does not keep it.
A recovery key in a response body is a secret, so Core Hub hands one out only to an interactive Super Admin session that reached Core Hub directly. The request is refused with 403 Forbidden and the code recovery_key_generation_not_allowed when it comes:
-
with a Personal API Token;
-
from an MCP tool call;
-
through Gluesync Connect, whose proxy records response bodies.
In these cases, generate the key with --generate-recovery-key and send its public key.
Rotate the recovery key
POST /backup/rotate-key takes the same optional body as /backup/enable, with the public key of the new recovery key, and has the same answers and refusals. Core Hub seals the next backups to the new key. The first backup under the new key is made when the interval of your plan allows it: up to a day on the free plan. The backups that already exist stay sealed to the old key. Keep every old key until the last backup sealed to it has aged out.
409 Conflict with the code backup_not_enabled means backups are off.
Read the status
curl -X GET https://your-gluesync.com/backup \
-H "Authorization: Bearer <your-token>"
{
"status": {
"state": "idle",
"enabled": true,
"reason": null,
"message": null,
"includeMetrics": false,
"keyId": "3fa9c01d5b7e2a64",
"lastAttemptAt": "2026-10-22T03:00:00Z",
"lastSuccessAt": "2026-10-22T03:00:41Z",
"lastBackupId": "bk_8d1f2c",
"lastError": null,
"consecutiveFailures": 0,
"nextRunAt": "2026-10-23T03:00:00Z",
"intervalSec": 86400
},
"policy": {
"enabled": true,
"reason": null,
"plan": "community",
"retentionDays": 5,
"maxSites": 1,
"siteSlotHeld": true,
"minIntervalSec": 86400,
"maxObjectBytes": 2147483648,
"maxSiteBytes": 21474836480,
"usedBytes": 183500800,
"backupCount": 5,
"latestBackupAt": "2026-10-22T03:00:41Z"
},
"backups": [
{
"id": "bk_8d1f2c",
"status": "complete",
"sizeBytes": 36700160,
"createdAt": "2026-10-22T03:00:02Z",
"completedAt": "2026-10-22T03:00:41Z",
"expiresAt": "2026-10-27T03:00:02Z",
"meta": {
"keyId": "3fa9c01d5b7e2a64",
"coreHubVersion": "2.3.1.0",
"schemaVersions": { "gluesync.db": 36 },
"contents": [ "gluesync.db", "bootstrap-core-hub.json", "gs-license.dat", "connections" ]
}
}
],
"connectError": null,
"warnings": []
}
| Field | Description |
|---|---|
|
The local state. See The status object. |
|
What Gluesync Connect says this site may do: the |
|
The latest backups that Gluesync Connect holds for this site, newest first. Each has an |
|
Why Gluesync Connect could not be asked, or |
|
Facts about the installation that are not errors, for example that the storage key is the default one or that Core Hub runs in DEBUG mode. |
The request makes a call to Gluesync Connect. It stores nothing and starts no backup.
The status object
| Field | Type | Description |
|---|---|---|
|
string |
|
|
boolean |
Whether backups are on. |
|
string or null |
Machine-readable cause behind |
|
string or null |
The same, in words. |
|
boolean |
Whether the metrics database is part of the backup. |
|
string or null |
The ID of the recovery key that backups are sealed to. |
|
string or null |
ISO-8601 times of the last attempt and of the last backup that reached Gluesync Connect. |
|
string or null |
The ID of the last backup that reached Gluesync Connect. |
|
string or null |
The reason the last attempt failed. |
|
integer |
How many attempts in a row failed. After 3, Core Hub raises one notification. |
|
string or null |
When the next backup is due. |
|
integer or null |
The spacing between backups that the plan allows, once Gluesync Connect has said so. |
Change the settings
curl -X PUT https://your-gluesync.com/backup/settings \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{"includeMetrics": true}'
| Field | Type | Description |
|---|---|---|
|
boolean, required |
Also back up the metrics database. It is large and rebuilt as the platform runs. Off by default. |
The body is limited to 4096 bytes. A field other than includeMetrics, a missing field or a value that is not true or false gives 400 Bad Request, and the field of the error names it. The answer is 200 OK with the backup status.
Run a backup now
curl -X POST https://your-gluesync.com/backup/run \
-H "Authorization: Bearer <your-token>"
The answer is 202 Accepted:
{ "started": true }
202 means that the backup was started, not that it finished. Poll GET /backup and read status.state and status.lastSuccessAt.
A manual backup obeys the same limit as a scheduled one. When the plan allows no backup yet, the answer is 429 Too Many Requests, with the Retry-After header in seconds and the time in the body:
{
"notificationId": -1,
"exceptionMessage": "The plan allows no backup before 2026-10-23T02:50:41Z.",
"message": "The plan allows no backup before 2026-10-23T02:50:41Z.",
"code": "backup_rate_limited",
"field": null,
"nextAllowedAt": "2026-10-23T02:50:41Z",
"retryAfterSec": 64241
}
Disable backups
POST /backup/disable turns backups off, cancels a backup in progress and removes the public key from Core Hub. The answer is 200 OK with the backup status. The backups in Gluesync Connect are kept until their retention ends.
/backup/disable and /backup/run take no body. An empty body or {} is accepted. Any other body gives 400 Bad Request.
Responses and errors
A refused request answers with the error body of the other Core Hub endpoints, plus a machine-readable code:
{
"notificationId": -1,
"exceptionMessage": "Backups are already enabled. Rotate the key to use a new recovery key.",
"message": "Backups are already enabled. Rotate the key to use a new recovery key.",
"code": "backup_already_enabled",
"field": null,
"nextAllowedAt": null,
"retryAfterSec": null
}
notificationId is always -1: a refused request is the answer to the caller, not an event of the platform, and raises no notification. field names the field of a 400. nextAllowedAt and retryAfterSec are set on a 429.
| Status | Code | Meaning |
|---|---|---|
|
The request completed and the body holds the result. |
|
|
|
|
|
|
The body is not valid. |
|
|
The Bearer token is missing, expired or not valid. |
|
|
The user is not a Super Admin. |
|
|
|
|
Core Hub did not answer in time. The body is plain text, not JSON. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The operation failed. The Core Hub log has the details. |
MCP tools
Two MCP tools cover the read and the run of a backup: get_backup_status and run_backup. Both need a Super Admin, and a read-only deployment does not expose run_backup. Turning backups on, rotating the key and turning them off are not available through MCP, Chronos or Bootstrapper, because a person must hold the recovery key. See Core Hub MCP server.