# Databases

> Managed Postgres, Valkey and MySQL, with backups, branching (with masking) and restores.

Managed Postgres, Valkey and MySQL, with backups, branching (with masking) and restores.

| Operation | Method and path |
| --- | --- |
| [Database plans](#database-plans) | `GET /v1/database-plans` |
| [List databases](#list-databases) | `GET /v1/projects/{project}/environments/{environment}/databases` |
| [Create a database](#create-a-database) | `POST /v1/projects/{project}/environments/{environment}/databases` |
| [Get a database](#get-a-database) | `GET /v1/projects/{project}/environments/{environment}/databases/{database}` |
| [Change plan, storage, extensions, masking or access](#change-plan-storage-extensions-masking-or-access) | `PATCH /v1/projects/{project}/environments/{environment}/databases/{database}` |
| [Delete a database (7-day soft delete)](#delete-a-database-7-day-soft-delete) | `DELETE /v1/projects/{project}/environments/{environment}/databases/{database}` |
| [Cancel a pending deletion](#cancel-a-pending-deletion) | `POST /v1/projects/{project}/environments/{environment}/databases/{database}/undelete` |
| [Connection credentials (gated, audited)](#connection-credentials-gated-audited) | `GET /v1/projects/{project}/environments/{environment}/databases/{database}/connection` |
| [Rotate credentials (rolling, dual-password window)](#rotate-credentials-rolling-dual-password-window) | `POST /v1/projects/{project}/environments/{environment}/databases/{database}/credentials/rotate` |
| [List branches](#list-branches) | `GET /v1/projects/{project}/environments/{environment}/databases/{database}/branches` |
| [Branch a database (copy-on-write clone, masked)](#branch-a-database-copy-on-write-clone-masked) | `POST /v1/projects/{project}/environments/{environment}/databases/{database}/branches` |
| [Get a branch](#get-a-branch) | `GET /v1/projects/{project}/environments/{environment}/databases/{database}/branches/{branch}` |
| [Delete a branch](#delete-a-branch) | `DELETE /v1/projects/{project}/environments/{environment}/databases/{database}/branches/{branch}` |
| [List backups](#list-backups) | `GET /v1/projects/{project}/environments/{environment}/databases/{database}/backups` |
| [Take a backup now](#take-a-backup-now) | `POST /v1/projects/{project}/environments/{environment}/databases/{database}/backups` |
| [Get a backup](#get-a-backup) | `GET /v1/projects/{project}/environments/{environment}/databases/{database}/backups/{backup_id}` |
| [Restore from a backup or point in time](#restore-from-a-backup-or-point-in-time) | `POST /v1/projects/{project}/environments/{environment}/databases/{database}/restore` |

## Database plans

```http
GET /v1/database-plans
```

- **Operation:** `listDatabasePlans`
- **Scopes:** none (public)
- **Paginated:** `?limit=` and `?cursor=`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `engine` | query | [DatabaseEngine](/docs/api/objects/database-engine/) |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | A page of DatabasePlan objects. | [DatabasePlanList](/docs/api/objects/database-plan-list/) |

**Errors:** `400`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/database-plans \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "data": [
    {
      "id": "starter",
      "engine": "postgres",
      "name": "HA",
      "description": "Primary plus a synchronous replica on another server, with automatic failover.",
      "instances": 1,
      "read_replicas": false,
      "pitr_days": 1,
      "daily_restore_test": false,
      "storage_price": {
        "amount": "7.42",
        "currency": "GBP",
        "display": "£7.42"
      },
      "available_on": [
        "trial"
      ]
    }
  ],
  "next_cursor": "string",
  "has_more": false
}
```

## List databases

```http
GET /v1/projects/{project}/environments/{environment}/databases
```

- **Operation:** `listDatabases`
- **Scopes:** `db:read`
- **Paginated:** `?limit=` and `?cursor=`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | A page of Database objects. | [DatabaseList](/docs/api/objects/database-list/) |

**Errors:** `400`, `401`, `403`, `404`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/projects/shopfront/environments/production/databases \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "data": [
    {
      "id": "db_01jbx2m9k7q4r8t5v6w3y0z1db",
      "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr",
      "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en",
      "name": "main",
      "engine": "postgres",
      "version": "18",
      "plan": "ha",
      "status": "available",
      "storage_gb": 50,
      "used_bytes": 1288490188,
      "pitr_days": 30,
      "extensions": [
        "pgvector",
        "pg_trgm"
      ],
      "maintenance_window": "Sun 02:00-04:00",
      "pooling": true,
      "public_access": {
        "enabled": false,
        "host": null,
        "port": null,
        "allow_ips": []
      },
      "internal_host": "main.db.guv.internal",
      "branch_of": null,
      "backups": {
        "last_backup_at": "2026-10-23T02:00:00Z",
        "last_restore_test_at": "2026-10-23T11:00:00Z",
        "last_restore_test_passed": true,
        "summary": "Backups on. Last restore test passed 3 h ago."
      },
      "deletion": null,
      "managed_by_manifest": true,
      "created_at": "2026-10-22T09:30:00Z",
      "updated_at": "2026-10-23T02:00:00Z"
    }
  ],
  "next_cursor": "string",
  "has_more": false
}
```

## Create a database

```http
POST /v1/projects/{project}/environments/{environment}/databases
```

- **Operation:** `createDatabase`
- **Scopes:** `db:create`
- **CLI:** `guv db create`
- **Approval:** In environments with protection turned on (usually production), changes by agents and by roles outside `approver_roles` return `approval_required`.
- **Idempotency-Key:** required; supports `?dry_run=true`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |

**Request body** (`application/json`, required): [DatabaseCreate](/docs/api/objects/database-create/)

| Field | Type | Description |
| --- | --- | --- |
| `name` * | string |  |
| `engine` * | [DatabaseEngine](/docs/api/objects/database-engine/) |  |
| `version` | string |  |
| `plan` | [DatabasePlanId](/docs/api/objects/database-plan-id/) |  |
| `storage_gb` | integer | Default `10`. |
| `pitr_days` | integer |  |
| `extensions` | array of string |  |
| `maintenance_window` | string |  |
| `masking` | [MaskingRules](/docs/api/objects/masking-rules/) |  |
| `pooling` | boolean | Default `true`. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `202` Accepted | Accepted. The work continues in the background; follow the operation. | [Operation](/docs/api/objects/operation/) |

**Errors:** `400`, `401`, `402`, `403`, `404`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/projects/shopfront/environments/production/databases \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "engine": "postgres",
  "version": "string",
  "plan": "starter",
  "storage_gb": 10,
  "pitr_days": 1,
  "extensions": [
    "string"
  ],
  "maintenance_window": "string",
  "masking": {
    "clients.full_name": "fake_name",
    "clients.nhs_number": null,
    "clients.date_of_birth": "shift_days(±30)",
    "carers.email": "hash_email"
  },
  "pooling": true
}'
```

**Example response**

```json
{
  "id": "op_01jbx2m9k7q4r8t5v6w3y0z1op",
  "kind": "deployment.create",
  "status": "running",
  "summary": "Deploying web to production",
  "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws",
  "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en",
  "resource": {
    "type": "deployment",
    "id": "dep_01jbx2m9k7q4r8t5v6w3y0z1dp",
    "url": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  },
  "progress": {
    "current_step": "build",
    "completed_steps": 1,
    "total_steps": 6,
    "percent": 20
  },
  "steps": [
    {
      "name": "resolve",
      "title": "Reading your settings",
      "status": "succeeded",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:00Z",
      "finished_at": "2026-10-23T14:02:01Z"
    },
    {
      "name": "build",
      "title": "Building your app",
      "status": "running",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:01Z",
      "finished_at": null
    }
  ],
  "created_by": {
    "type": "user",
    "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
    "display": "Stewart Webb"
  },
  "created_at": "2026-10-23T14:02:00Z",
  "links": {
    "self": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op",
    "events": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op/events",
    "resource": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  }
}
```

## Get a database

```http
GET /v1/projects/{project}/environments/{environment}/databases/{database}
```

- **Operation:** `getDatabase`
- **Scopes:** `db:read`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [Database](/docs/api/objects/database/) |

**Errors:** `400`, `401`, `403`, `404`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "id": "db_01jbx2m9k7q4r8t5v6w3y0z1db",
  "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en",
  "name": "main",
  "engine": "postgres",
  "version": "18",
  "plan": "ha",
  "status": "available",
  "storage_gb": 50,
  "used_bytes": 1288490188,
  "pitr_days": 30,
  "extensions": [
    "pgvector",
    "pg_trgm"
  ],
  "maintenance_window": "Sun 02:00-04:00",
  "pooling": true,
  "public_access": {
    "enabled": false,
    "host": null,
    "port": null,
    "allow_ips": []
  },
  "internal_host": "main.db.guv.internal",
  "branch_of": null,
  "backups": {
    "last_backup_at": "2026-10-23T02:00:00Z",
    "last_restore_test_at": "2026-10-23T11:00:00Z",
    "last_restore_test_passed": true,
    "summary": "Backups on. Last restore test passed 3 h ago."
  },
  "deletion": null,
  "managed_by_manifest": true,
  "created_at": "2026-10-22T09:30:00Z",
  "updated_at": "2026-10-23T02:00:00Z"
}
```

## Change plan, storage, extensions, masking or access

```http
PATCH /v1/projects/{project}/environments/{environment}/databases/{database}
```

Exposing publicly, shortening backup retention and downgrading HA always need a human.

- **Operation:** `updateDatabase`
- **Scopes:** `db:create`
- **Approval:** In environments with protection turned on (usually production), changes by agents and by roles outside `approver_roles` return `approval_required`.
- **Idempotency-Key:** required; supports `?dry_run=true`
- **Concurrency:** send the `ETag` you read as `If-Match`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |

**Request body** (`application/json`, required): [DatabaseUpdate](/docs/api/objects/database-update/)

| Field | Type | Description |
| --- | --- | --- |
| `plan` | [DatabasePlanId](/docs/api/objects/database-plan-id/) |  |
| `storage_gb` | integer | Grow only. |
| `pitr_days` | integer | Shortening retention always needs human approval. |
| `extensions` | array of string |  |
| `maintenance_window` | string |  |
| `masking` | [MaskingRules](/docs/api/objects/masking-rules/) |  |
| `pooling` | boolean |  |
| `public_access` | object | Exposing a database publicly always needs human approval and an IP allowlist on Hobby. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `202` Accepted | Accepted. The work continues in the background; follow the operation. | [Operation](/docs/api/objects/operation/) |

**Errors:** `400`, `401`, `402`, `403`, `404`, `409`, `412`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X PATCH https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "plan": "starter",
  "storage_gb": 1,
  "pitr_days": 1,
  "extensions": [
    "string"
  ],
  "maintenance_window": "string",
  "masking": {
    "clients.full_name": "fake_name",
    "clients.nhs_number": null,
    "clients.date_of_birth": "shift_days(±30)",
    "carers.email": "hash_email"
  },
  "pooling": false,
  "public_access": {
    "enabled": false,
    "allow_ips": [
      "string"
    ]
  }
}'
```

**Example response**

```json
{
  "id": "op_01jbx2m9k7q4r8t5v6w3y0z1op",
  "kind": "deployment.create",
  "status": "running",
  "summary": "Deploying web to production",
  "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws",
  "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en",
  "resource": {
    "type": "deployment",
    "id": "dep_01jbx2m9k7q4r8t5v6w3y0z1dp",
    "url": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  },
  "progress": {
    "current_step": "build",
    "completed_steps": 1,
    "total_steps": 6,
    "percent": 20
  },
  "steps": [
    {
      "name": "resolve",
      "title": "Reading your settings",
      "status": "succeeded",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:00Z",
      "finished_at": "2026-10-23T14:02:01Z"
    },
    {
      "name": "build",
      "title": "Building your app",
      "status": "running",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:01Z",
      "finished_at": null
    }
  ],
  "created_by": {
    "type": "user",
    "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
    "display": "Stewart Webb"
  },
  "created_at": "2026-10-23T14:02:00Z",
  "links": {
    "self": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op",
    "events": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op/events",
    "resource": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  }
}
```

## Delete a database (7-day soft delete)

```http
DELETE /v1/projects/{project}/environments/{environment}/databases/{database}
```

Requires `confirm=<name>` and, for humans, a step-up passkey. Agents can never delete databases; they always get `approval_required`.

- **Operation:** `deleteDatabase`
- **Scopes:** `projects:delete`
- **Approval:** Always needs a human approval with a passkey, whoever asks, including agents with full scopes.
- **Idempotency-Key:** required; supports `?dry_run=true`
- **Concurrency:** send the `ETag` you read as `If-Match`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |
| `confirm` | query | string | Typed confirmation for destructive actions. Must equal the resource name (or slug/hostname). Missing or wrong values return `confirmation_required`. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `202` Accepted | Accepted. The work continues in the background; follow the operation. | [Operation](/docs/api/objects/operation/) |

**Errors:** `400`, `401`, `403`, `404`, `409`, `412`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X DELETE https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```

**Example response**

```json
{
  "id": "op_01jbx2m9k7q4r8t5v6w3y0z1op",
  "kind": "deployment.create",
  "status": "running",
  "summary": "Deploying web to production",
  "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws",
  "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en",
  "resource": {
    "type": "deployment",
    "id": "dep_01jbx2m9k7q4r8t5v6w3y0z1dp",
    "url": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  },
  "progress": {
    "current_step": "build",
    "completed_steps": 1,
    "total_steps": 6,
    "percent": 20
  },
  "steps": [
    {
      "name": "resolve",
      "title": "Reading your settings",
      "status": "succeeded",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:00Z",
      "finished_at": "2026-10-23T14:02:01Z"
    },
    {
      "name": "build",
      "title": "Building your app",
      "status": "running",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:01Z",
      "finished_at": null
    }
  ],
  "created_by": {
    "type": "user",
    "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
    "display": "Stewart Webb"
  },
  "created_at": "2026-10-23T14:02:00Z",
  "links": {
    "self": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op",
    "events": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op/events",
    "resource": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  }
}
```

## Cancel a pending deletion

```http
POST /v1/projects/{project}/environments/{environment}/databases/{database}/undelete
```

- **Operation:** `undeleteDatabase`
- **Scopes:** `db:create`
- **Idempotency-Key:** required; supports `?dry_run=true`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `202` Accepted | Accepted. The work continues in the background; follow the operation. | [Operation](/docs/api/objects/operation/) |

**Errors:** `400`, `401`, `403`, `404`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main/undelete \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```

**Example response**

```json
{
  "id": "op_01jbx2m9k7q4r8t5v6w3y0z1op",
  "kind": "deployment.create",
  "status": "running",
  "summary": "Deploying web to production",
  "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws",
  "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en",
  "resource": {
    "type": "deployment",
    "id": "dep_01jbx2m9k7q4r8t5v6w3y0z1dp",
    "url": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  },
  "progress": {
    "current_step": "build",
    "completed_steps": 1,
    "total_steps": 6,
    "percent": 20
  },
  "steps": [
    {
      "name": "resolve",
      "title": "Reading your settings",
      "status": "succeeded",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:00Z",
      "finished_at": "2026-10-23T14:02:01Z"
    },
    {
      "name": "build",
      "title": "Building your app",
      "status": "running",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:01Z",
      "finished_at": null
    }
  ],
  "created_by": {
    "type": "user",
    "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
    "display": "Stewart Webb"
  },
  "created_at": "2026-10-23T14:02:00Z",
  "links": {
    "self": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op",
    "events": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op/events",
    "resource": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  }
}
```

## Connection credentials (gated, audited)

```http
GET /v1/projects/{project}/environments/{environment}/databases/{database}/connection
```

Needs `db:connect`. Agent tokens get `approval_required` in protected environments. Every read is in the audit log.

- **Operation:** `getDatabaseConnection`
- **Scopes:** `db:connect`
- **CLI:** `guv db connect`
- **Approval:** In environments with protection turned on (usually production), changes by agents and by roles outside `approver_roles` return `approval_required`.

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [DatabaseConnection](/docs/api/objects/database-connection/) |

**Errors:** `400`, `401`, `403`, `404`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main/connection \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "database_id": "string",
  "host": "string",
  "port": 1,
  "user": "string",
  "password": "string",
  "database": "string",
  "url": "string",
  "direct_url": "string",
  "sslmode": "require",
  "expires_at": "2026-10-22T13:05:00Z"
}
```

## Rotate credentials (rolling, dual-password window)

```http
POST /v1/projects/{project}/environments/{environment}/databases/{database}/credentials/rotate
```

- **Operation:** `rotateDatabaseCredentials`
- **Scopes:** `db:create`
- **Approval:** In environments with protection turned on (usually production), changes by agents and by roles outside `approver_roles` return `approval_required`.
- **Idempotency-Key:** required; supports `?dry_run=true`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `202` Accepted | Accepted. The work continues in the background; follow the operation. | [Operation](/docs/api/objects/operation/) |

**Errors:** `400`, `401`, `403`, `404`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main/credentials/rotate \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```

**Example response**

```json
{
  "id": "op_01jbx2m9k7q4r8t5v6w3y0z1op",
  "kind": "deployment.create",
  "status": "running",
  "summary": "Deploying web to production",
  "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws",
  "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en",
  "resource": {
    "type": "deployment",
    "id": "dep_01jbx2m9k7q4r8t5v6w3y0z1dp",
    "url": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  },
  "progress": {
    "current_step": "build",
    "completed_steps": 1,
    "total_steps": 6,
    "percent": 20
  },
  "steps": [
    {
      "name": "resolve",
      "title": "Reading your settings",
      "status": "succeeded",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:00Z",
      "finished_at": "2026-10-23T14:02:01Z"
    },
    {
      "name": "build",
      "title": "Building your app",
      "status": "running",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:01Z",
      "finished_at": null
    }
  ],
  "created_by": {
    "type": "user",
    "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
    "display": "Stewart Webb"
  },
  "created_at": "2026-10-23T14:02:00Z",
  "links": {
    "self": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op",
    "events": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op/events",
    "resource": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  }
}
```

## List branches

```http
GET /v1/projects/{project}/environments/{environment}/databases/{database}/branches
```

- **Operation:** `listDatabaseBranches`
- **Scopes:** `db:read`
- **Paginated:** `?limit=` and `?cursor=`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | A page of DatabaseBranch objects. | [DatabaseBranchList](/docs/api/objects/database-branch-list/) |

**Errors:** `400`, `401`, `403`, `404`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main/branches \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "data": [
    {
      "id": "dbb_01jbx2m9k7q4r8t5v6w3y0z1pr",
      "name": "string",
      "source_database_id": "string",
      "database_id": "string",
      "environment_id": "string",
      "from_time": "2026-10-22T13:05:00Z",
      "masked": false,
      "masking": {
        "clients.full_name": "fake_name",
        "clients.nhs_number": null,
        "clients.date_of_birth": "shift_days(±30)",
        "carers.email": "hash_email"
      },
      "status": "creating",
      "created_at": "2026-10-22T13:05:00Z"
    }
  ],
  "next_cursor": "string",
  "has_more": false
}
```

## Branch a database (copy-on-write clone, masked)

```http
POST /v1/projects/{project}/environments/{environment}/databases/{database}/branches
```

Clones in seconds and applies masking rules before the branch is attached. `masking=none` is refused for special-category projects.

- **Operation:** `createDatabaseBranch`
- **Scopes:** `db:branch`
- **CLI:** `guv db branch`
- **Idempotency-Key:** required; supports `?dry_run=true`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |

**Request body** (`application/json`, required): [DatabaseBranchCreate](/docs/api/objects/database-branch-create/)

| Field | Type | Description |
| --- | --- | --- |
| `name` * | string |  |
| `target_environment` | string | Environment to attach the branch to. Defaults to the source environment. |
| `point_in_time` | string (date-time) |  |
| `masking` | `default`, `none` or [MaskingRules](/docs/api/objects/masking-rules/) | `default` uses the database's masking rules. `none` is refused for special-category projects. Default `default`. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `202` Accepted | Accepted. The work continues in the background; follow the operation. | [Operation](/docs/api/objects/operation/) |

**Errors:** `400`, `401`, `402`, `403`, `404`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main/branches \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "target_environment": "string",
  "point_in_time": "2026-10-22T13:05:00Z",
  "masking": "default"
}'
```

**Example response**

```json
{
  "id": "op_01jbx2m9k7q4r8t5v6w3y0z1op",
  "kind": "deployment.create",
  "status": "running",
  "summary": "Deploying web to production",
  "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws",
  "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en",
  "resource": {
    "type": "deployment",
    "id": "dep_01jbx2m9k7q4r8t5v6w3y0z1dp",
    "url": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  },
  "progress": {
    "current_step": "build",
    "completed_steps": 1,
    "total_steps": 6,
    "percent": 20
  },
  "steps": [
    {
      "name": "resolve",
      "title": "Reading your settings",
      "status": "succeeded",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:00Z",
      "finished_at": "2026-10-23T14:02:01Z"
    },
    {
      "name": "build",
      "title": "Building your app",
      "status": "running",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:01Z",
      "finished_at": null
    }
  ],
  "created_by": {
    "type": "user",
    "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
    "display": "Stewart Webb"
  },
  "created_at": "2026-10-23T14:02:00Z",
  "links": {
    "self": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op",
    "events": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op/events",
    "resource": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  }
}
```

## Get a branch

```http
GET /v1/projects/{project}/environments/{environment}/databases/{database}/branches/{branch}
```

- **Operation:** `getDatabaseBranch`
- **Scopes:** `db:read`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |
| `branch` * | path | string | Database branch id (`dbb_…`) or name. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [DatabaseBranch](/docs/api/objects/database-branch/) |

**Errors:** `400`, `401`, `403`, `404`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main/branches/feature-x \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "id": "dbb_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "name": "string",
  "source_database_id": "string",
  "database_id": "string",
  "environment_id": "string",
  "from_time": "2026-10-22T13:05:00Z",
  "masked": false,
  "masking": {
    "clients.full_name": "fake_name",
    "clients.nhs_number": null,
    "clients.date_of_birth": "shift_days(±30)",
    "carers.email": "hash_email"
  },
  "status": "creating",
  "created_at": "2026-10-22T13:05:00Z"
}
```

## Delete a branch

```http
DELETE /v1/projects/{project}/environments/{environment}/databases/{database}/branches/{branch}
```

- **Operation:** `deleteDatabaseBranch`
- **Scopes:** `db:branch`
- **Idempotency-Key:** required; supports `?dry_run=true`
- **Concurrency:** send the `ETag` you read as `If-Match`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |
| `branch` * | path | string | Database branch id (`dbb_…`) or name. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `202` Accepted | Accepted. The work continues in the background; follow the operation. | [Operation](/docs/api/objects/operation/) |

**Errors:** `400`, `401`, `403`, `404`, `409`, `412`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X DELETE https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main/branches/feature-x \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```

**Example response**

```json
{
  "id": "op_01jbx2m9k7q4r8t5v6w3y0z1op",
  "kind": "deployment.create",
  "status": "running",
  "summary": "Deploying web to production",
  "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws",
  "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en",
  "resource": {
    "type": "deployment",
    "id": "dep_01jbx2m9k7q4r8t5v6w3y0z1dp",
    "url": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  },
  "progress": {
    "current_step": "build",
    "completed_steps": 1,
    "total_steps": 6,
    "percent": 20
  },
  "steps": [
    {
      "name": "resolve",
      "title": "Reading your settings",
      "status": "succeeded",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:00Z",
      "finished_at": "2026-10-23T14:02:01Z"
    },
    {
      "name": "build",
      "title": "Building your app",
      "status": "running",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:01Z",
      "finished_at": null
    }
  ],
  "created_by": {
    "type": "user",
    "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
    "display": "Stewart Webb"
  },
  "created_at": "2026-10-23T14:02:00Z",
  "links": {
    "self": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op",
    "events": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op/events",
    "resource": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  }
}
```

## List backups

```http
GET /v1/projects/{project}/environments/{environment}/databases/{database}/backups
```

- **Operation:** `listDatabaseBackups`
- **Scopes:** `db:read`
- **Paginated:** `?limit=` and `?cursor=`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | A page of DatabaseBackup objects. | [DatabaseBackupList](/docs/api/objects/database-backup-list/) |

**Errors:** `400`, `401`, `403`, `404`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main/backups \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "data": [
    {
      "id": "bak_01jbx2m9k7q4r8t5v6w3y0z1pr",
      "database_id": "string",
      "kind": "base",
      "trigger": "scheduled",
      "status": "running",
      "size_bytes": 1,
      "locations": [
        "uk-primary"
      ],
      "restore_test": {
        "status": "passed",
        "at": "2026-10-22T13:05:00Z"
      },
      "started_at": "2026-10-22T13:05:00Z",
      "finished_at": "2026-10-22T13:05:00Z",
      "expires_at": "2026-10-22T13:05:00Z"
    }
  ],
  "next_cursor": "string",
  "has_more": false
}
```

## Take a backup now

```http
POST /v1/projects/{project}/environments/{environment}/databases/{database}/backups
```

- **Operation:** `createDatabaseBackup`
- **Scopes:** `db:create`
- **Idempotency-Key:** required; supports `?dry_run=true`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |

**Request body** (`application/json`): [BackupCreate](/docs/api/objects/backup-create/)

| Field | Type | Description |
| --- | --- | --- |
| `note` | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `202` Accepted | Accepted. The work continues in the background; follow the operation. | [Operation](/docs/api/objects/operation/) |

**Errors:** `400`, `401`, `403`, `404`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main/backups \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "note": "string"
}'
```

**Example response**

```json
{
  "id": "op_01jbx2m9k7q4r8t5v6w3y0z1op",
  "kind": "deployment.create",
  "status": "running",
  "summary": "Deploying web to production",
  "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws",
  "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en",
  "resource": {
    "type": "deployment",
    "id": "dep_01jbx2m9k7q4r8t5v6w3y0z1dp",
    "url": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  },
  "progress": {
    "current_step": "build",
    "completed_steps": 1,
    "total_steps": 6,
    "percent": 20
  },
  "steps": [
    {
      "name": "resolve",
      "title": "Reading your settings",
      "status": "succeeded",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:00Z",
      "finished_at": "2026-10-23T14:02:01Z"
    },
    {
      "name": "build",
      "title": "Building your app",
      "status": "running",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:01Z",
      "finished_at": null
    }
  ],
  "created_by": {
    "type": "user",
    "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
    "display": "Stewart Webb"
  },
  "created_at": "2026-10-23T14:02:00Z",
  "links": {
    "self": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op",
    "events": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op/events",
    "resource": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  }
}
```

## Get a backup

```http
GET /v1/projects/{project}/environments/{environment}/databases/{database}/backups/{backup_id}
```

- **Operation:** `getDatabaseBackup`
- **Scopes:** `db:read`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |
| `backup_id` * | path | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [DatabaseBackup](/docs/api/objects/database-backup/) |

**Errors:** `400`, `401`, `403`, `404`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main/backups/bac_01jbx2m9k7q4r8t5v6w3y0z1pr \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "id": "bak_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "database_id": "string",
  "kind": "base",
  "trigger": "scheduled",
  "status": "running",
  "size_bytes": 1,
  "locations": [
    "uk-primary"
  ],
  "restore_test": {
    "status": "passed",
    "at": "2026-10-22T13:05:00Z"
  },
  "started_at": "2026-10-22T13:05:00Z",
  "finished_at": "2026-10-22T13:05:00Z",
  "expires_at": "2026-10-22T13:05:00Z"
}
```

## Restore from a backup or point in time

```http
POST /v1/projects/{project}/environments/{environment}/databases/{database}/restore
```

Restoring `in_place` takes a pre-restore backup first and always needs human approval in production.

- **Operation:** `restoreDatabase`
- **Scopes:** `db:restore`
- **Approval:** In environments with protection turned on (usually production), changes by agents and by roles outside `approver_roles` return `approval_required`.
- **Idempotency-Key:** required; supports `?dry_run=true`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `project` * | path | string | Project id (`prj_…`) or slug (resolved in the `Guv-Workspace` workspace, or the caller's default). |
| `environment` * | path | string | Environment id (`env_…`) or name. |
| `database` * | path | string | Database id (`db_…`) or name. |

**Request body** (`application/json`, required): [DatabaseRestoreRequest](/docs/api/objects/database-restore-request/)

| Field | Type | Description |
| --- | --- | --- |
| `backup_id` | string |  |
| `point_in_time` | string (date-time) |  |
| `target` * | `new_database`, `in_place` | `in_place` takes a pre-restore backup first and always needs human approval in production. |
| `new_name` | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `202` Accepted | Accepted. The work continues in the background; follow the operation. | [Operation](/docs/api/objects/operation/) |

**Errors:** `400`, `401`, `403`, `404`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/projects/shopfront/environments/production/databases/main/restore \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "backup_id": "string",
  "point_in_time": "2026-10-22T13:05:00Z",
  "target": "new_database",
  "new_name": "string"
}'
```

**Example response**

```json
{
  "id": "op_01jbx2m9k7q4r8t5v6w3y0z1op",
  "kind": "deployment.create",
  "status": "running",
  "summary": "Deploying web to production",
  "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws",
  "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en",
  "resource": {
    "type": "deployment",
    "id": "dep_01jbx2m9k7q4r8t5v6w3y0z1dp",
    "url": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  },
  "progress": {
    "current_step": "build",
    "completed_steps": 1,
    "total_steps": 6,
    "percent": 20
  },
  "steps": [
    {
      "name": "resolve",
      "title": "Reading your settings",
      "status": "succeeded",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:00Z",
      "finished_at": "2026-10-23T14:02:01Z"
    },
    {
      "name": "build",
      "title": "Building your app",
      "status": "running",
      "attempt": 1,
      "started_at": "2026-10-23T14:02:01Z",
      "finished_at": null
    }
  ],
  "created_by": {
    "type": "user",
    "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
    "display": "Stewart Webb"
  },
  "created_at": "2026-10-23T14:02:00Z",
  "links": {
    "self": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op",
    "events": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op/events",
    "resource": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp"
  }
}
```
