scribasedocs

Control API /v1

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).

JSON
{
  "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.

Terminal
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.

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

JSON
{
  "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

TypeScript
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.