# Postgres

> Databases Rytiv runs for you: create, branch, fork, restore, expire, fetch a copy's connection URL, delete.

A database is a resource of kind `postgres`: PostgreSQL on a machine of its own with its data on a volume, reachable only from the apps you attach it to, always over TLS. Every database can be branched (a copy-on-write copy on the same host, ready in seconds) or forked (a full copy from its archive, on any host, optionally from a moment in the past). See the guide [Postgres](https://docs.rytiv.com/postgres.md).

The resource routes also accept `kind: "redis"`; the fields that only apply to Postgres say so.

## Endpoints

- [`POST /v1/resources`](#post-v1resources): Creates a database. The answer carries the connection URL with its password, and only this answer does
- [`GET /v1/resources`](#get-v1resources): The organization's databases, never a URL
- [`GET /v1/resources/:name`](#get-v1resourcesname): One database with its state
- [`PATCH /v1/resources/:name`](#patch-v1resourcesname): Changes what can change later: the expiry of a copy, and the sleep policy
- [`DELETE /v1/resources/:name`](#delete-v1resourcesname): Deletes the database, its machine and its volume, and detaches it from every app. It cannot be undone
- [`POST /v1/resources/:name/branch`](#post-v1resourcesnamebranch): Creates a copy-on-write branch of the database as it is now. Answers `202` with the branch in state `restoring`
- [`POST /v1/resources/:name/fork`](#post-v1resourcesnamefork): Creates a full copy from the database's archive, optionally as of a moment in the past. Answers `202`
- [`POST /v1/resources/:name/restore`](#post-v1resourcesnamerestore): Puts the database back to a moment in time in place; the state before is kept as a branch
- [`POST /v1/resources/:name/url`](#post-v1resourcesnameurl): Hands out the connection URL of a branch or fork exactly once

## POST /v1/resources

Creates a database. The answer carries the connection URL with its password, and only this answer does.

**Auth:** API key with full access (a key with scopes cannot call this route). An agent token of class `sandbox` or `twin` may call it; class `production` is refused (`agent-token-production-readonly`).

### Request body

JSON, the shape `CreateResourceRequest` of the contract.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | required | The database's name, like an app name. Unique across Rytiv. |
| `kind` | `redis` \| `postgres` | required | `postgres` or `redis`. |
| `memoryMb` | integer | optional | Memory of the database's machine. 128 to 16384. |
| `persistent` | boolean | optional | Whether the data lives on a volume. Default: yes for Postgres. |
| `sizeGb` | integer | optional | Size of the data volume in GB. 1 to 500. |
| `flushIntervalS` | integer | optional | How often the volume is copied to durable storage. 15 to 86400. |
| `durability` | `standard` \| `host-redundant` \| `zone-redundant` | optional | `standard`: one machine. `host-redundant`: a standby on a second host. Postgres only; `zone-redundant` is in the shape but refused at creation. |
| `locale` | string | optional | The ICU locale for sorting and comparing text. Fixed once the database exists. Postgres only. |
| `suspendPolicy` | object | optional | When the database sleeps. Postgres only; `{"mode": "off"}` keeps it always on. |
| `suspendPolicy.mode` | `suspend` \| `stop` \| `off` | optional | `suspend` keeps the memory and resumes fast (machines up to 2048 MB; larger ones stop instead), `stop` shuts down, `off` never sleeps. default `"suspend"`. |
| `suspendPolicy.idleSeconds` | integer | optional | Seconds without requests and without CPU load before the machine sleeps. 10 to 86400, default `300`. |
| `suspendPolicy.stopAfterSeconds` | integer | optional | Second stage: after this many seconds since the last activity a suspended machine is stopped (its memory snapshot dropped, its disk kept). Only with `mode: "suspend"`, and greater than `idleSeconds`. 10 to 604800. |

### Example request

```bash
curl -X POST "https://api.rytiv.app/v1/resources" \
  -H "Authorization: Bearer $RYTIV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"my-db","kind":"postgres"}'
```

### Response

`201` with a JSON object of these fields:

| Field | Type | Description |
| --- | --- | --- |
| `name` | string |  |
| `kind` | string |  |
| `port` | integer |  |
| `secretKey` | string | the name of the secret an attached app gets, `DATABASE_URL` |
| `url` | string | the connection URL with its password; returned here only |

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `bad-request` | The request is malformed or a field is out of range. `error` names every field; `issues` lists them. |
| 400 | `name-reserved` | The platform keeps this name (for example `api`, `console`, `docs`, `www`, and every name that is `rytiv` or starts with `rytiv-`). |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |
| 409 | `conflict` | The request conflicts with the current state. |
| 409 | `name-taken` | The name is used somewhere on Rytiv (an app, a database, a volume or a bucket). |
| 503 | `no-capacity` | No host in the region has room for the machine right now; try again. |

## GET /v1/resources

The organization's databases, never a URL.

**Auth:** API key with full access or the scope `read`. Every class of agent token may call it.

### Example request

```bash
curl -X GET "https://api.rytiv.app/v1/resources" \
  -H "Authorization: Bearer $RYTIV_TOKEN"
```

### Response

`200` with a list of databases, each:

```json
[
  {
    "name": "my-db",
    "orgId": "<orgId>",
    "kind": "postgres",
    "port": -9007199254740991,
    "secretKey": "<secretKey>",
    "durability": "standard",
    "createdAt": "2026-10-21T09:00:00Z",
    "state": "restoring"
  }
]
```

| Field | Type | Present | Description |
| --- | --- | --- | --- |
| `name` | string | always | The database's name. |
| `orgId` | string | always | The organization that owns it. |
| `kind` | `redis` \| `postgres` | always | `postgres` or `redis`. |
| `port` | integer | always | The port the database listens on. -9007199254740991 to 9007199254740991. |
| `secretKey` | string | always | The name of the secret an attached app gets, `DATABASE_URL`. |
| `durability` | `standard` \| `host-redundant` \| `zone-redundant` | always | `standard`: one machine. `host-redundant`: a standby on a second host. default `"standard"`. |
| `primaryMachineId` | string | if set | Redundant databases: the machine that is primary now. |
| `createdAt` | string (ISO 8601) | always | When it was created. |
| `state` | `restoring` \| `ready` \| `failed` | always | `ready`, or for a copy `restoring` until it is, or `failed`. default `"ready"`. |
| `stateReason` | string | if set | Why a copy ended `failed`. |
| `sourceName` | string | if set | Copies: the database it was made from. |
| `sourceAsOf` | string (ISO 8601) | if set | Copies: the moment of the source's data. |
| `copyKind` | `fork` \| `branch` | if set | `fork` or `branch`. |
| `ephemeral` | `true` | if set | Ephemeral branches only: `true`. |
| `ephemeralNote` | string | if set | The same, as a sentence. |
| `suspendPolicy` | object | if set | When the database sleeps. |
| `suspendPolicy.mode` | `suspend` \| `stop` \| `off` | always | `suspend` keeps the memory and resumes fast (machines up to 2048 MB; larger ones stop instead), `stop` shuts down, `off` never sleeps. default `"suspend"`. |
| `suspendPolicy.idleSeconds` | integer | always | Seconds without requests and without CPU load before the machine sleeps. 10 to 86400, default `300`. |
| `suspendPolicy.stopAfterSeconds` | integer | if set | Second stage: after this many seconds since the last activity a suspended machine is stopped (its memory snapshot dropped, its disk kept). Only with `mode: "suspend"`, and greater than `idleSeconds`. 10 to 604800. |
| `expiresAt` | string (ISO 8601) | if set | Copies: when the platform deletes it. |

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |

## GET /v1/resources/:name

One database with its state.

**Auth:** API key with full access (a key with scopes cannot call this route). Every class of agent token may call it.

### Path parameters

| Name | Description |
| --- | --- |
| `name` | The database's name. |

### Example request

```bash
curl -X GET "https://api.rytiv.app/v1/resources/<name>" \
  -H "Authorization: Bearer $RYTIV_TOKEN"
```

### Response

`200` with the database:

```json
{
  "name": "my-db",
  "orgId": "<orgId>",
  "kind": "postgres",
  "port": -9007199254740991,
  "secretKey": "<secretKey>",
  "durability": "standard",
  "createdAt": "2026-10-21T09:00:00Z",
  "state": "restoring"
}
```

| Field | Type | Present | Description |
| --- | --- | --- | --- |
| `name` | string | always | The database's name. |
| `orgId` | string | always | The organization that owns it. |
| `kind` | `redis` \| `postgres` | always | `postgres` or `redis`. |
| `port` | integer | always | The port the database listens on. -9007199254740991 to 9007199254740991. |
| `secretKey` | string | always | The name of the secret an attached app gets, `DATABASE_URL`. |
| `durability` | `standard` \| `host-redundant` \| `zone-redundant` | always | `standard`: one machine. `host-redundant`: a standby on a second host. default `"standard"`. |
| `primaryMachineId` | string | if set | Redundant databases: the machine that is primary now. |
| `createdAt` | string (ISO 8601) | always | When it was created. |
| `state` | `restoring` \| `ready` \| `failed` | always | `ready`, or for a copy `restoring` until it is, or `failed`. default `"ready"`. |
| `stateReason` | string | if set | Why a copy ended `failed`. |
| `sourceName` | string | if set | Copies: the database it was made from. |
| `sourceAsOf` | string (ISO 8601) | if set | Copies: the moment of the source's data. |
| `copyKind` | `fork` \| `branch` | if set | `fork` or `branch`. |
| `ephemeral` | `true` | if set | Ephemeral branches only: `true`. |
| `ephemeralNote` | string | if set | The same, as a sentence. |
| `suspendPolicy` | object | if set | When the database sleeps. |
| `suspendPolicy.mode` | `suspend` \| `stop` \| `off` | always | `suspend` keeps the memory and resumes fast (machines up to 2048 MB; larger ones stop instead), `stop` shuts down, `off` never sleeps. default `"suspend"`. |
| `suspendPolicy.idleSeconds` | integer | always | Seconds without requests and without CPU load before the machine sleeps. 10 to 86400, default `300`. |
| `suspendPolicy.stopAfterSeconds` | integer | if set | Second stage: after this many seconds since the last activity a suspended machine is stopped (its memory snapshot dropped, its disk kept). Only with `mode: "suspend"`, and greater than `idleSeconds`. 10 to 604800. |
| `expiresAt` | string (ISO 8601) | if set | Copies: when the platform deletes it. |

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |
| 404 | `not-found` | Not found, or not yours. An object of another organization answers like one that does not exist. |

## PATCH /v1/resources/:name

Changes what can change later: the expiry of a copy, and the sleep policy.

**Auth:** API key with full access (a key with scopes cannot call this route). An agent token of class `sandbox` or `twin` may call it; class `production` is refused (`agent-token-production-readonly`).

### Path parameters

| Name | Description |
| --- | --- |
| `name` | The database's name. |

### Request body

JSON, the shape `UpdateResourceRequest` of the contract.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `suspendPolicy` | object | optional | When the machine sleeps. Without it, a new app never sleeps; a deploy without it keeps the app's rule. |
| `suspendPolicy.mode` | `suspend` \| `stop` \| `off` | optional | `suspend` keeps the memory and resumes fast (machines up to 2048 MB; larger ones stop instead), `stop` shuts down, `off` never sleeps. default `"suspend"`. |
| `suspendPolicy.idleSeconds` | integer | optional | Seconds without requests and without CPU load before the machine sleeps. 10 to 86400, default `300`. |
| `suspendPolicy.stopAfterSeconds` | integer | optional | Second stage: after this many seconds since the last activity a suspended machine is stopped (its memory snapshot dropped, its disk kept). Only with `mode: "suspend"`, and greater than `idleSeconds`. 10 to 604800. |
| `expiresAt` | string (ISO 8601) or null | optional | When the copy is deleted (a moment). Not together with `ttlSeconds`. |
| `ttlSeconds` | integer | optional | Lifetime from now, in seconds. 60 to 2592000. |

### Example request

```bash
curl -X PATCH "https://api.rytiv.app/v1/resources/<name>" \
  -H "Authorization: Bearer $RYTIV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"suspendPolicy":{"mode":"suspend"}}'
```

### Response

`200` with the database:

```json
{
  "name": "my-db",
  "orgId": "<orgId>",
  "kind": "postgres",
  "port": -9007199254740991,
  "secretKey": "<secretKey>",
  "durability": "standard",
  "createdAt": "2026-10-21T09:00:00Z",
  "state": "restoring"
}
```

| Field | Type | Present | Description |
| --- | --- | --- | --- |
| `name` | string | always | The database's name. |
| `orgId` | string | always | The organization that owns it. |
| `kind` | `redis` \| `postgres` | always | `postgres` or `redis`. |
| `port` | integer | always | The port the database listens on. -9007199254740991 to 9007199254740991. |
| `secretKey` | string | always | The name of the secret an attached app gets, `DATABASE_URL`. |
| `durability` | `standard` \| `host-redundant` \| `zone-redundant` | always | `standard`: one machine. `host-redundant`: a standby on a second host. default `"standard"`. |
| `primaryMachineId` | string | if set | Redundant databases: the machine that is primary now. |
| `createdAt` | string (ISO 8601) | always | When it was created. |
| `state` | `restoring` \| `ready` \| `failed` | always | `ready`, or for a copy `restoring` until it is, or `failed`. default `"ready"`. |
| `stateReason` | string | if set | Why a copy ended `failed`. |
| `sourceName` | string | if set | Copies: the database it was made from. |
| `sourceAsOf` | string (ISO 8601) | if set | Copies: the moment of the source's data. |
| `copyKind` | `fork` \| `branch` | if set | `fork` or `branch`. |
| `ephemeral` | `true` | if set | Ephemeral branches only: `true`. |
| `ephemeralNote` | string | if set | The same, as a sentence. |
| `suspendPolicy` | object | if set | When the database sleeps. |
| `suspendPolicy.mode` | `suspend` \| `stop` \| `off` | always | `suspend` keeps the memory and resumes fast (machines up to 2048 MB; larger ones stop instead), `stop` shuts down, `off` never sleeps. default `"suspend"`. |
| `suspendPolicy.idleSeconds` | integer | always | Seconds without requests and without CPU load before the machine sleeps. 10 to 86400, default `300`. |
| `suspendPolicy.stopAfterSeconds` | integer | if set | Second stage: after this many seconds since the last activity a suspended machine is stopped (its memory snapshot dropped, its disk kept). Only with `mode: "suspend"`, and greater than `idleSeconds`. 10 to 604800. |
| `expiresAt` | string (ISO 8601) | if set | Copies: when the platform deletes it. |

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `bad-request` | The request is malformed or a field is out of range. `error` names every field; `issues` lists them. |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |
| 404 | `not-found` | Not found, or not yours. An object of another organization answers like one that does not exist. |

## DELETE /v1/resources/:name

Deletes the database, its machine and its volume, and detaches it from every app. It cannot be undone.

**Auth:** API key with full access (a key with scopes cannot call this route). An agent token of class `sandbox` or `twin` may call it; class `production` is refused (`agent-token-production-readonly`).

### Path parameters

| Name | Description |
| --- | --- |
| `name` | The database's name. |

### Example request

```bash
curl -X DELETE "https://api.rytiv.app/v1/resources/<name>" \
  -H "Authorization: Bearer $RYTIV_TOKEN"
```

### Response

`204` with no body.

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |
| 404 | `not-found` | Not found, or not yours. An object of another organization answers like one that does not exist. |

## POST /v1/resources/:name/branch

Creates a copy-on-write branch of the database as it is now. Answers `202` with the branch in state `restoring`.

**Auth:** API key with full access (a key with scopes cannot call this route). An agent token of class `sandbox` or `twin` may call it; class `production` is refused (`agent-token-production-readonly`).

### Path parameters

| Name | Description |
| --- | --- |
| `name` | The database's name. |

### Request body

JSON, the shape `BranchResourceRequest` of the contract.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | required | The name of the new branch. |
| `ephemeral` | boolean | optional | Ready faster, without an archive of its own; lost if its host fails. |
| `expiresAt` | string (ISO 8601) | optional | When the copy is deleted (a moment). Not together with `ttlSeconds`. |
| `ttlSeconds` | integer | optional | When the copy is deleted, as seconds from now. 60 to 2592000. |

### Example request

```bash
curl -X POST "https://api.rytiv.app/v1/resources/<name>/branch" \
  -H "Authorization: Bearer $RYTIV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"my-db-try"}'
```

### Response

`202` with the branch, in state `restoring`:

```json
{
  "name": "my-db",
  "orgId": "<orgId>",
  "kind": "postgres",
  "port": -9007199254740991,
  "secretKey": "<secretKey>",
  "durability": "standard",
  "createdAt": "2026-10-21T09:00:00Z",
  "state": "restoring"
}
```

| Field | Type | Present | Description |
| --- | --- | --- | --- |
| `name` | string | always | The database's name. |
| `orgId` | string | always | The organization that owns it. |
| `kind` | `redis` \| `postgres` | always | `postgres` or `redis`. |
| `port` | integer | always | The port the database listens on. -9007199254740991 to 9007199254740991. |
| `secretKey` | string | always | The name of the secret an attached app gets, `DATABASE_URL`. |
| `durability` | `standard` \| `host-redundant` \| `zone-redundant` | always | `standard`: one machine. `host-redundant`: a standby on a second host. default `"standard"`. |
| `primaryMachineId` | string | if set | Redundant databases: the machine that is primary now. |
| `createdAt` | string (ISO 8601) | always | When it was created. |
| `state` | `restoring` \| `ready` \| `failed` | always | `ready`, or for a copy `restoring` until it is, or `failed`. default `"ready"`. |
| `stateReason` | string | if set | Why a copy ended `failed`. |
| `sourceName` | string | if set | Copies: the database it was made from. |
| `sourceAsOf` | string (ISO 8601) | if set | Copies: the moment of the source's data. |
| `copyKind` | `fork` \| `branch` | if set | `fork` or `branch`. |
| `ephemeral` | `true` | if set | Ephemeral branches only: `true`. |
| `ephemeralNote` | string | if set | The same, as a sentence. |
| `suspendPolicy` | object | if set | When the database sleeps. |
| `suspendPolicy.mode` | `suspend` \| `stop` \| `off` | always | `suspend` keeps the memory and resumes fast (machines up to 2048 MB; larger ones stop instead), `stop` shuts down, `off` never sleeps. default `"suspend"`. |
| `suspendPolicy.idleSeconds` | integer | always | Seconds without requests and without CPU load before the machine sleeps. 10 to 86400, default `300`. |
| `suspendPolicy.stopAfterSeconds` | integer | if set | Second stage: after this many seconds since the last activity a suspended machine is stopped (its memory snapshot dropped, its disk kept). Only with `mode: "suspend"`, and greater than `idleSeconds`. 10 to 604800. |
| `expiresAt` | string (ISO 8601) | if set | Copies: when the platform deletes it. |

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `bad-request` | The request is malformed or a field is out of range. `error` names every field; `issues` lists them. |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |
| 404 | `not-found` | Not found, or not yours. An object of another organization answers like one that does not exist. |
| 409 | `name-taken` | The name is used somewhere on Rytiv (an app, a database, a volume or a bucket). |

## POST /v1/resources/:name/fork

Creates a full copy from the database's archive, optionally as of a moment in the past. Answers `202`.

**Auth:** API key with full access (a key with scopes cannot call this route). An agent token of class `sandbox` or `twin` may call it; class `production` is refused (`agent-token-production-readonly`).

### Path parameters

| Name | Description |
| --- | --- |
| `name` | The database's name. |

### Request body

JSON, the shape `ForkResourceRequest` of the contract.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | required | The name of the new fork. |
| `asOf` | string (ISO 8601) | optional | A moment in the past to copy or restore from (ISO 8601). Without it, now. |
| `expiresAt` | string (ISO 8601) | optional | When the copy is deleted (a moment). Not together with `ttlSeconds`. |
| `ttlSeconds` | integer | optional | When the copy is deleted, as seconds from now. 60 to 2592000. |

### Example request

```bash
curl -X POST "https://api.rytiv.app/v1/resources/<name>/fork" \
  -H "Authorization: Bearer $RYTIV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"my-db-yesterday"}'
```

### Response

`202` with the fork, in state `restoring`:

```json
{
  "name": "my-db",
  "orgId": "<orgId>",
  "kind": "postgres",
  "port": -9007199254740991,
  "secretKey": "<secretKey>",
  "durability": "standard",
  "createdAt": "2026-10-21T09:00:00Z",
  "state": "restoring"
}
```

| Field | Type | Present | Description |
| --- | --- | --- | --- |
| `name` | string | always | The database's name. |
| `orgId` | string | always | The organization that owns it. |
| `kind` | `redis` \| `postgres` | always | `postgres` or `redis`. |
| `port` | integer | always | The port the database listens on. -9007199254740991 to 9007199254740991. |
| `secretKey` | string | always | The name of the secret an attached app gets, `DATABASE_URL`. |
| `durability` | `standard` \| `host-redundant` \| `zone-redundant` | always | `standard`: one machine. `host-redundant`: a standby on a second host. default `"standard"`. |
| `primaryMachineId` | string | if set | Redundant databases: the machine that is primary now. |
| `createdAt` | string (ISO 8601) | always | When it was created. |
| `state` | `restoring` \| `ready` \| `failed` | always | `ready`, or for a copy `restoring` until it is, or `failed`. default `"ready"`. |
| `stateReason` | string | if set | Why a copy ended `failed`. |
| `sourceName` | string | if set | Copies: the database it was made from. |
| `sourceAsOf` | string (ISO 8601) | if set | Copies: the moment of the source's data. |
| `copyKind` | `fork` \| `branch` | if set | `fork` or `branch`. |
| `ephemeral` | `true` | if set | Ephemeral branches only: `true`. |
| `ephemeralNote` | string | if set | The same, as a sentence. |
| `suspendPolicy` | object | if set | When the database sleeps. |
| `suspendPolicy.mode` | `suspend` \| `stop` \| `off` | always | `suspend` keeps the memory and resumes fast (machines up to 2048 MB; larger ones stop instead), `stop` shuts down, `off` never sleeps. default `"suspend"`. |
| `suspendPolicy.idleSeconds` | integer | always | Seconds without requests and without CPU load before the machine sleeps. 10 to 86400, default `300`. |
| `suspendPolicy.stopAfterSeconds` | integer | if set | Second stage: after this many seconds since the last activity a suspended machine is stopped (its memory snapshot dropped, its disk kept). Only with `mode: "suspend"`, and greater than `idleSeconds`. 10 to 604800. |
| `expiresAt` | string (ISO 8601) | if set | Copies: when the platform deletes it. |

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `bad-request` | The request is malformed or a field is out of range. `error` names every field; `issues` lists them. |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |
| 404 | `not-found` | Not found, or not yours. An object of another organization answers like one that does not exist. |
| 409 | `name-taken` | The name is used somewhere on Rytiv (an app, a database, a volume or a bucket). |

## POST /v1/resources/:name/restore

Puts the database back to a moment in time in place; the state before is kept as a branch.

**Auth:** API key with full access (a key with scopes cannot call this route). An agent token of class `sandbox` or `twin` may call it; class `production` is refused (`agent-token-production-readonly`).

### Path parameters

| Name | Description |
| --- | --- |
| `name` | The database's name. |

### Request body

JSON, the shape `RestoreResourceRequest` of the contract.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `asOf` | string (ISO 8601) | required | A moment in the past to copy or restore from (ISO 8601). Without it, now. |
| `backupName` | string | optional | The name of the branch that keeps the state before the restore. Default: `<name>-old-<timestamp>`. |

### Example request

```bash
curl -X POST "https://api.rytiv.app/v1/resources/<name>/restore" \
  -H "Authorization: Bearer $RYTIV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"asOf":"2026-10-21T09:00:00Z"}'
```

### Response

A JSON body on success.

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `bad-request` | The request is malformed or a field is out of range. `error` names every field; `issues` lists them. |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |
| 404 | `not-found` | Not found, or not yours. An object of another organization answers like one that does not exist. |

## POST /v1/resources/:name/url

Hands out the connection URL of a branch or fork exactly once.

**Auth:** API key with full access (a key with scopes cannot call this route). An agent token of class `sandbox` or `twin` may call it; class `production` is refused.

### Path parameters

| Name | Description |
| --- | --- |
| `name` | The name of the branch or fork. |

### Example request

```bash
curl -X POST "https://api.rytiv.app/v1/resources/<name>/url" \
  -H "Authorization: Bearer $RYTIV_TOKEN"
```

### Response

`200` with the connection URL:

```json
{
  "name": "my-app",
  "url": "<url>"
}
```

| Field | Type | Present | Description |
| --- | --- | --- | --- |
| `name` | string | always |  |
| `url` | string | always | The connection URL with its password. Handed out once. |

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |
| 404 | `not-found` | Not found, or not yours. An object of another organization answers like one that does not exist. |
| 409 | `not-a-copy` | The database is neither a fork nor a branch; its URL came with its create answer. |
| 409 | `not-ready` | The copy is still restoring, or failed; there is nothing to hand out yet. |
| 409 | `url-handed-out` | The URL of this copy was handed out already. It leaves the platform exactly once. |
