---
title: HTTPS API reference
description: Requests, responses, scopes and failure behavior for the workspace Secrets API.
---

Base URL: `https://app.asyncflux.com/api/secrets`. Paths below are relative to this base. All endpoints require authentication. Use a workspace automation token in `Authorization: Bearer` as shown in [Connect a machine or agent](connect.md).

A token selects one workspace. URL-encode path components. Browser integrations use their signed-in session, the selected workspace ID in `X-Vikings-Workspace`, and the session's CSRF token for writes; do not reuse a browser cookie as a machine credential.

Send `Content-Type: application/json` on every POST, PATCH and PUT, including read operations implemented as POST. Reveal requires a JSON body; `{}` is sufficient. Untrusted browser origins and cross-site browser requests are refused.

## Permissions and responses

The tables show scopes for **scoped API tokens**. Metadata requires an eligible workspace actor; mutation and plaintext operations require an owner/admin actor and the corresponding live grants. Scope names do not include an environment. See [Permissions](permissions.md) for legacy credentials and role constraints.

Unless stated otherwise, success is HTTP `200` with a JSON object. Create operations below return `201`; successful deletes return `204` without a response body. Request schema failures return `422`. Check both the HTTP status and per-item results on bulk operations.

There is no GET-one-plaintext endpoint. Use list for metadata, and an explicit reveal request for plaintext. Reveals, exports and comparisons with `reveal: true` carry `Cache-Control: no-store`; the client must still handle their output carefully.

## Status and types

| Method | Path | Scope | Result |
| --- | --- | --- | --- |
| GET | `/status` | `secrets:read` | `available`, `status`, `health` and, when permitted, `workspace`; service failures may include `detail` |
| GET | `/types` | `secrets:read` | `types`: entries with `id`, `label`, `redaction` |

`/status` reports an unavailable Secrets service as `available: false`, rather than a service-unavailable HTTP `503`. Authentication and authorization can still fail. Its diagnostic payload may contain deployment details; do not post it publicly without reviewing it.

The HTTPS types catalog lists `opaque`, `token`, `connection_string`, `private_key`, `json_credential`, `certificate` and `publishable`. `connection_string` uses partial redaction; `certificate` and `publishable` are unmasked; the other listed types are fully masked.

The underlying service also supports `config`, an unmasked configuration value. Existing `config` entries can appear in lists even though this type is currently omitted from the HTTPS types catalog and dashboard selector. Do not use the catalog as proof that all returned display values are masked.

## Workspace and environments

| Method | Path | Scope | Request and result |
| --- | --- | --- | --- |
| GET | `/workspace` | `secrets:read` | Returns `slug`, `name`, `registered`, `envs` |
| POST | `/workspace` | `secrets:edit` | Body `{}` or `{"envs":["dev","prd"]}`; returns workspace description, `201` |
| POST | `/workspace/envs` | `secrets:edit` | Body `{"name":"docs-demo"}`; returns workspace description, `201` |
| DELETE | `/workspace/envs/{env}` | `secrets:edit` | Optional `?force=true`; `204` |

Each `envs` entry in the workspace response has `name` and `count`. Before Secrets is enabled, `registered` is false and `envs` is empty. Omitted, null or empty `envs` on enable selects `dev` and `prd`. Enabling an already registered workspace returns `409`. The name `compare` is reserved by the HTTPS interface.

Removing a nonempty environment requires `force=true`, which deletes its secrets. The service refuses removal of the last environment. Review the target carefully before using force. This API has no workspace creation, workspace rename or environment rename route.

## List and create secrets

`GET /envs/{env}/secrets` requires `secrets:read` and returns:

```json
{
  "workspace": "docs-demo",
  "env": "dev",
  "secrets": [
    {
      "name": "DOCS_EXAMPLE_TOKEN",
      "workspace": "docs-demo",
      "env": "dev",
      "namespace": "docs-demo/dev",
      "type": "token",
      "version": 1,
      "display": "***"
    }
  ]
}
```

This is an illustrative subset of metadata fields. Rows are sorted by name and can also include `note`, `created_at`, `updated_at`, `updated_by`, `rotate_via` and `last_rotated`. Optional fields may be absent. `display` follows the type's redaction policy and can contain plaintext. Names and notes are visible metadata.

`POST /envs/{env}/secrets` requires `secrets:edit`. Required fields are `name` and `value`, both strings. Optional fields are `type` and `note`.

```json
{
  "name": "DOCS_EXAMPLE_TOKEN",
  "value": "example-only-not-a-credential",
  "type": "token",
  "note": "Disposable documentation example"
}
```

Success returns `201` with `{"secret":{"name":"DOCS_EXAMPLE_TOKEN","type":"token","type_inferred":false}}`. Omitting type lets the service infer it. Values must satisfy their type's validation; an empty value cannot create a secret. For names that also work with `.env` and JSON import, use letters, digits and underscores, starting with a letter or underscore, such as `DOCS_EXAMPLE_TOKEN`.

Creating a secret with a note performs the create and note update separately. If the second operation fails, the secret may already exist. Check metadata before retrying a failed request.

## Update, rename and delete

`PATCH /envs/{env}/secrets/{name}` requires `secrets:edit`. All fields are optional:

| Field | Behavior |
| --- | --- |
| `value` | Replacement string; omission or null keeps the stored value |
| `type` | A nonempty type changes the recorded type; omission keeps it |
| `note` | Omission keeps the note; null or `""` clears it; text sets it |
| `new_name` | A nonempty different name renames the secret before other edits |

Example body:

```json
{"new_name":"DOCS_RENAMED_TOKEN","note":"Disposable renamed example"}
```

Returns `{"secret":{"name":"DOCS_RENAMED_TOKEN"}}`, with `type` also present when a value/type/note update was performed. A rename and subsequent update are separate operations; an update failure can leave the rename applied. Refresh metadata before retrying. A type-only change to weaker redaction requires re-entering the value and can otherwise be refused. Do not send `""` to erase a value: empty-value updates are not a supported way to clear secrets.

`DELETE /envs/{env}/secrets/{name}` requires `secrets:edit` and returns `204`. Deletion removes the secret; it is not a historical-version restore operation.

## Reveal a value

`POST /envs/{env}/secrets/{name}/reveal` requires `secrets:values` and an owner/admin actor. Send `{}` for the current value, or `{"version":1}` for a retained version. Omitted, null or zero version selects current.

```json
{
  "name": "DOCS_EXAMPLE_TOKEN",
  "type": "token",
  "version": 1,
  "value": "example-only-not-a-credential"
}
```

The example is disposable. Real responses contain plaintext. Reveal is audited and returns `Cache-Control: no-store`.

## History and rollback

`GET /envs/{env}/secrets/{name}/history` requires `secrets:read`. It returns `name`, `current_version` and `versions`. Each version row contains `version`, `updated_at`, `updated_by`, `client_id`, `actor`, `replaced_by` and `current`. Fields without recorded data can be null.

The current version comes first; earlier versions follow in descending version order. `updated_by` identifies the version's author when known. `replaced_by` identifies who replaced an older version, not who originally wrote it. The history response contains no values. History retention depends on service configuration; do not assume every former value is retained.

`POST /envs/{env}/secrets/{name}/rollback` requires **both** `secrets:edit` and `secrets:values`, plus owner/admin authority. Body: `{"version":1}`. `version` is required; zero selects the newest retained previous version. Prefer an explicit version chosen from history.

Returns `{"version":3}` for an illustrative new current version. Rollback restores a historical value as a new version, retaining the current type. It is a write, not a read or a renumbering of history.

## Save multiple changes

`POST /envs/{env}/changes` requires `secrets:edit`. Each top-level array defaults to empty:

```json
{
  "deletes": [],
  "renames": [{"name":"DOCS_EXAMPLE_TOKEN","new_name":"DOCS_RENAMED_TOKEN"}],
  "upserts": [{"name":"DOCS_RENAMED_TOKEN","value":"example-only-replacement","type":"token"}],
  "notes": [{"name":"DOCS_RENAMED_TOKEN","note":"Disposable example"}]
}
```

The application order is deletes, renames, upserts carrying values, upserts without values, then note edits. Upserts with values use an overwriting import; upserts without values update type or note on an existing secret. Use `notes` with `note: ""` to clear a note explicitly. A rename failure skips dependent edits to its intended new name.

```json
{
  "applied": 1,
  "results": [
    {"op":"rename","name":"DOCS_EXAMPLE_TOKEN","ok":true},
    {"op":"upsert","name":"DOCS_RENAMED_TOKEN","ok":false,"error":"Example rejection"}
  ]
}
```

This illustrative partial result still has HTTP `200`. Inspect every `ok` flag. Failures do not undo successful items. If the service is unavailable and no item applied, the request returns `503` instead. Re-read state after a timeout before retrying; the operation may have reached the service.

## Import and export

`POST /envs/{env}/import` requires `secrets:edit`:

```json
{
  "format": "env",
  "content": "DOCS_EXAMPLE_TOKEN=example-only-not-a-credential\n",
  "overwrite": false
}
```

`content` is required. `format` is `env` (default) or `json`; `overwrite` defaults to false. JSON input is an object of names to values. String values are used directly; non-null objects, arrays, numbers and booleans are converted to JSON strings. Null values and invalid names produce item errors.

The `.env` parser accepts blank lines, comments, an optional `export` prefix and single/double-quoted multiline values. Single quotes are literal. Double quotes recognize newline, carriage-return, tab, quote and backslash escapes. Duplicate names keep the last parsed value. Unquoted inline comments begin at whitespace followed by `#`.

Returns `created`, `updated`, `skipped` and `errors` arrays. Error entries have `name` and `message`. Parse errors and item errors are combined; valid items can still be written. With overwrite disabled, existing names are skipped. This is not an atomic import.

`POST /envs/{env}/export` requires `secrets:values`. Body `{}` defaults to `env`; `{"format":"json"}` or `{"format":"yaml"}` selects another output. Returns `{"format":"json","content":"..."}` with `Cache-Control: no-store`. The `content` string contains plaintext name/value pairs, sorted by name. It does not include notes, history or access rules and is not a full service backup. Every export is audited.

## Compare environments

`POST /compare` requires `secrets:read`:

```json
{"configs":[{"env":"dev"},{"env":"prd"}],"reveal":false}
```

`configs` is required and must contain at least one entry; the dashboard asks for two or more. Each entry has `env`. `reveal` defaults to false. Returns `workspace`, `configs` in request order and name-sorted `rows`.

Each row has `name`, `cells` in configuration order and `differs`. A cell contains `present`, `type`, `version`, `updated_at` and `display`. Missing cells have `present: false` and null metadata. Default comparison checks presence, type and display; two masked values can appear equal while their underlying values differ.

With `reveal: true`, **also require `secrets:values`** and an owner/admin actor. Present cells include `value`; comparison uses actual values in place of display. This exports each environment, is audited once per export, and returns `Cache-Control: no-store`.

## Audit activity

`GET /envs/{env}/activity` requires `secrets:read`. Optional query parameters:

| Parameter | Meaning |
| --- | --- |
| `name` | Filter to one secret name |
| `action` | Filter by an action identifier, for example `secret_reveal` |
| `tail` | Recent-entry limit; defaults to 200 and is clamped to 1–1000 |

Returns `{"entries":[...]}`. Entries include `timestamp`, `action` and `success`; `namespace`, `secret_name`, `client_id`, `actor`, `lease_id` and `details` can also appear. Audit records are designed to describe the action without including its value. Do not place credentials in names or other metadata.

API activity is attributed with a token credential ID and actor ID; browser activity uses the dashboard actor ID. The daemon's `client_id` is its separately authenticated service identity; `actor` carries the bridge's attribution. Attribution is not an extra authorization grant.

## Authentication endpoints

These paths are relative to `https://app.asyncflux.com/api`, outside the Secrets base:

| Method | Path | Access and result |
| --- | --- | --- |
| GET | `/work/auth/token-self` | API token; returns only that credential's metadata, never its secret |
| GET | `/work/auth/token-options` | Human workspace owner/admin; workspace, eligible actors, permission catalog and expiry limits |
| GET | `/work/auth/tokens` | Human workspace owner/admin; `items` of token metadata |
| POST | `/work/auth/tokens` | Human workspace owner/admin; creates a token, `201`, one-time `token` disclosure |
| POST | `/work/auth/tokens/{token_id}/revoke` | Human workspace owner/admin; returns `id`, `revoked`, `closed_streams` |
| GET | `/work/auth/secrets-skill` | Human workspace owner/admin; downloads the maintained agent instructions |

For scoped creation, send `name` (1–120 characters, not blank), nonempty `scopes`, optional `actor_id`, and optional `expires_in_seconds` (60–31536000; default 2592000). Example: `{"name":"docs-demo-client","scopes":["secrets:read"],"expires_in_seconds":3600}`. The human session must supply the selected workspace header and CSRF for writes. Scoped tokens cannot call these management endpoints or mint more tokens.

## Errors

Authentication errors contain `error`, `code` and a correlation identifier; domain errors contain `error` and may include details. Validation errors can use FastAPI's `detail` list. Do not assume every error has the same JSON shape.

| Status | Check |
| --- | --- |
| `400` | Missing workspace selection, reserved environment name or invalid request context |
| `401` | Missing, invalid, expired or revoked credential |
| `403` | Role, grant, module scope or browser-origin refusal |
| `404` | Resource not found or workspace not visible to the caller |
| `409` | Secrets already enabled for this workspace |
| `413` | Request exceeds the Secrets service message limit |
| `415` | A POST/PATCH/PUT body was not sent as JSON |
| `422` | Invalid request fields or a service parameter rejection |
| `501` | The deployed Secrets service does not support this operation |
| `502` | Other Secrets service error; review its message rather than assuming a transient network fault |
| `503` | The API bridge cannot reach the Secrets service |

[Safe troubleshooting](troubleshooting.md) explains what to collect without disclosing a credential or value.
