Backups & restore
Every environment has durable backup records. A backup is a PostgreSQL
custom-format archive whose readability is checked with pg_restore --list and
whose bytes are fingerprinted with SHA-256. A restore replays a backup into a
target environment and records evidence: how many relations and
RLS-protected relations were restored, and the SHA-256 of the evidence record —
so "the backup works" is something you can read, not assume.
Archives are produced by the scribase-backup operator tool (see
Run in your own cluster); these routes manage the records, trigger
new backups, and run restores.
GET /v1/organizations/{org}/projects/{project}/environments/{env}/backups
Lists the environment's backups, newest first. Optional ?limit= (1–200).
{
"items": [
{
"backup_id": "nightly-2026-09-23",
"environment": { "organization_id": "acme", "project_id": "store", "environment_id": "production" },
"status": "available",
"format": "custom",
"size_bytes": 482133504,
"created_at_epoch_seconds": 1790121600,
"sha256": "9f2b...",
"object": { "bucket": "scribase-backups", "key": "scribase/acme/store/production/9f2b....dump", "uploaded_at_epoch_seconds": 1790121900 },
"verified_at_epoch_seconds": 1790122200
}
]
}status is pending, available, failed, or deleted. sha256, object,
and verified_at_epoch_seconds appear once the archive exists, is uploaded, and
is verified.
POST /v1/organizations/{org}/projects/{project}/environments/{env}/backups
Records a new pending backup. The Idempotency-Key header becomes the backup
ID, so retrying the same request never creates a second backup.
curl -X POST "$SCRIBASE_API_URL/v1/organizations/acme/projects/store/environments/production/backups" \
-H "Authorization: Bearer $SCRIBASE_ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: manual-2026-09-23' \
-d '{}'GET /v1/organizations/{org}/projects/{project}/backups/{backup_id}?environment={env}
Reads one backup. A backup ID is unique within its environment, so the environment is a required query parameter.
POST /v1/organizations/{org}/projects/{project}/backups/{backup_id}/restore?environment={env}
Restores the backup from env into target_environment_id in the same
project. Headers required: Idempotency-Key.
curl -X POST "$SCRIBASE_API_URL/v1/organizations/acme/projects/store/backups/nightly-2026-09-23/restore?environment=production" \
-H "Authorization: Bearer $SCRIBASE_ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: restore-drill-2026-09-23' \
-d '{"target_environment_id": "restore-drill"}'Response:
{
"id": "rst_...",
"source": { "organization_id": "acme", "project_id": "store", "environment_id": "production" },
"target": { "organization_id": "acme", "project_id": "store", "environment_id": "restore-drill" },
"source_backup_id": "nightly-2026-09-23",
"status": "restored",
"created_at_epoch_seconds": 1790125000,
"relation_count": 64,
"rls_relation_count": 18,
"evidence_sha256": "c41a...",
"restored_at_epoch_seconds": 1790125410
}status is pending, restored, or failed. The evidence fields are present
once the restore has completed.
Restore into a separate environment first (a restore drill), check the evidence and your application against it, then promote — never restore over production blind.
SDK
const ref = { organizationId: 'acme', projectId: 'store', environmentId: 'production' };
const backups = await scribase.backups.list(ref);
const backup = await scribase.backups.create(ref, { backupId: 'manual-2026-09-23' });
const { restore } = await scribase.restores.start(ref, {
backupId: 'nightly-2026-09-23',
targetEnvironmentId: 'restore-drill',
});Console
Each environment's Backups page lists backups with their verification state and starts restores.