# Secrets

> Encrypted environment variables of an app: set, list names, remove, and choose which ones a build may see.

Secrets are environment variables stored encrypted. The API lists their names and never their values. A secret reaches the app's machines at their next start; with `"staged": false` Rytiv rolls the app right away. Each secret carries a `build` choice: whether the next source build sees it as environment, or only the running machines do.

Secrets that Rytiv manages for you (the `DATABASE_URL` of an attached database, the `S3_*` keys of an attached bucket) cannot be changed or removed here; detach the resource instead.

## Endpoints

- [`GET /v1/apps/:name/secrets`](#get-v1appsnamesecrets): The app's secrets: names, when they changed, whether Rytiv manages them, and whether a build sees them. Never a value
- [`PUT /v1/apps/:name/secrets`](#put-v1appsnamesecrets): Sets secrets. `staged: true` (the default) applies them at the next deploy; `staged: false` rolls the app now. `build` chooses per key whether the next build sees it
- [`DELETE /v1/apps/:name/secrets/:key`](#delete-v1appsnamesecretskey): Removes one secret

## GET /v1/apps/:name/secrets

The app's secrets: names, when they changed, whether Rytiv manages them, and whether a build sees them. Never a value.

**Auth:** API key with full access or the scope `deploy` or `deploy:<app>` for this app, or the scope `read`. Every class of agent token may call it.

### Path parameters

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

### Example request

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

### Response

`200` with a list of secrets, each:

```json
[
  {
    "name": "my-app",
    "updatedAt": "2026-10-21T09:00:00Z",
    "managed": true
  }
]
```

| Field | Type | Present | Description |
| --- | --- | --- | --- |
| `name` | string | always | The secret's name. |
| `updatedAt` | string (ISO 8601) | always | When it last changed. |
| `managed` | boolean | always | Whether Rytiv manages it (an attachment or a database password); then it cannot be changed here. |
| `managedReason` | `attachment` \| `resource-password` | if set | `attachment` or `resource-password`. |
| `build` | boolean | if set | Whether the next source build sees 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. |

## PUT /v1/apps/:name/secrets

Sets secrets. `staged: true` (the default) applies them at the next deploy; `staged: false` rolls the app now. `build` chooses per key whether the next build sees it.

**Auth:** API key with full access or the scope `deploy` or `deploy:<app>` for this app. 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 app's name. |

### Request body

JSON, the shape `SetSecretsRequest` of the contract.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `secrets` | object of string | optional | Names and values to set. Names like `MY_SECRET`; the platform's own `RYTIV_` names are refused. |
| `staged` | boolean | optional | `true` (default): the values apply at the next deploy. `false`: Rytiv rolls the app now if a machine lacks a new value. default `true`. |
| `build` | object of boolean | optional | Per secret name, whether the next source build sees it as environment. |

### Example request

```bash
curl -X PUT "https://api.rytiv.app/v1/apps/<name>/secrets" \
  -H "Authorization: Bearer $RYTIV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"secrets":{"<key>":"<value>"}}'
```

### Response

`200` with a JSON body.

### 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 | `org-limit-computers` | The organization has as many computers as it may have. The body carries `limit` and `current`. Delete one, or contact us for more. |
| 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/apps/:name/secrets/:key

Removes one secret.

**Auth:** API key with full access or the scope `deploy` or `deploy:<app>` for this app. 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 app's name. |
| `key` | The secret's name. |

### Example request

```bash
curl -X DELETE "https://api.rytiv.app/v1/apps/<name>/secrets/<key>" \
  -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. |
