> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usesink.co/llms.txt
> Use this file to discover all available pages before exploring further.

# API resource reference

> Manage workspaces, teams, projects, environments, and secrets.

Every endpoint here needs an API key or session token. Path segments in braces are UUIDs returned by the parent resource — list your way down, or read them out of `.sink.json`.

```text theme={null}
workspace → team → project → environment → secret
```

## Workspaces

| Method   | Path                                 | Notes                                                                |
| -------- | ------------------------------------ | -------------------------------------------------------------------- |
| `GET`    | `/workspaces/`                       | Workspaces this account belongs to, each with the plan governing it. |
| `POST`   | `/workspaces/`                       | `{ "name": "Acme", "avatar_url": "https://…" }`                      |
| `PATCH`  | `/workspaces/{workspace_id}`         | `name` and/or `avatar_url`. Owner only.                              |
| `DELETE` | `/workspaces/{workspace_id}`         | Owner only.                                                          |
| `GET`    | `/workspaces/{workspace_id}/members` | Members with their roles.                                            |

## Teams

| Method   | Path                                       | Notes                                                                          |
| -------- | ------------------------------------------ | ------------------------------------------------------------------------------ |
| `GET`    | `/teams/{workspace_id}/`                   | Owners and admins see every team; everyone else sees the teams they belong to. |
| `POST`   | `/teams/{workspace_id}/`                   | `{ "name": "Platform" }`. Owner or admin only. `409` on a duplicate name.      |
| `GET`    | `/teams/{workspace_id}/{team_id}`          | One team.                                                                      |
| `PATCH`  | `/teams/{workspace_id}/{team_id}`          | `name` and/or `avatar_url`.                                                    |
| `DELETE` | `/teams/{workspace_id}/{team_id}`          | Remove a team.                                                                 |
| `GET`    | `/teams/{workspace_id}/{team_id}/projects` | Projects under a team.                                                         |

## Projects

Base path: `/projects/{workspace_id}/{team_id}`

| Method   | Path            | Notes                                                                               |
| -------- | --------------- | ----------------------------------------------------------------------------------- |
| `GET`    | `/`             | List projects.                                                                      |
| `POST`   | `/`             | `{ "name": "checkout-api", "proj_metadata": {} }`. A slug is derived from the name. |
| `PATCH`  | `/{project_id}` | `is_active` and/or `proj_metadata`.                                                 |
| `DELETE` | `/{project_id}` | Remove a project.                                                                   |

## Environments

Base path: `/environments/{workspace_id}/{team_id}/{project_id}`

| Method   | Path                 | Notes                                               |
| -------- | -------------------- | --------------------------------------------------- |
| `GET`    | `/`                  | List environments.                                  |
| `POST`   | `/`                  | Create one.                                         |
| `PATCH`  | `/{environment_id}/` | `{ "name": "staging" }`                             |
| `DELETE` | `/{environment_id}`  | Deletes the environment **and every secret in it**. |

```json Create an environment theme={null}
{
  "name": "production",
  "environment_type": "prod",
  "default_policy": 90
}
```

<ParamField body="environment_type" type="string" required>
  One of `dev`, `staging`, `prod`, `pilot`, `custom`. Nothing else is accepted.
</ParamField>

<ParamField body="default_policy" type="integer">
  Rotation cadence in days for the secrets inside. Falls back to the deployment default when omitted. See [Audit & rotation](/security/audit#rotation).
</ParamField>

## Secrets

Set the base path once:

```text theme={null}
/secrets/{workspace_id}/{team_id}/{project_id}/{environment_id}
```

| Method   | Path                    | Notes                                                                                                |
| -------- | ----------------------- | ---------------------------------------------------------------------------------------------------- |
| `POST`   | `/`                     | One secret: `{ "key": "DATABASE_URL", "value": "…", "description": "Primary DB" }`.                  |
| `POST`   | `/bulk`                 | Many: `{ "secrets": [{ "key": "A", "value": "1" }] }`. Returns `201`.                                |
| `GET`    | `/`                     | List and decrypt. `?version=n` reads each secret at that version.                                    |
| `GET`    | `/{secret_id}`          | One decrypted secret.                                                                                |
| `PATCH`  | `/{secret_id}`          | `key`, `value` and/or `description`. A new value mints a new version.                                |
| `PATCH`  | `/`                     | Bulk update, matching on `key`: `{ "secrets": [{ "key": "OLD", "new_key": "NEW", "value": "…" }] }`. |
| `DELETE` | `/{secret_id}`          | Delete one secret, with its versions and log.                                                        |
| `DELETE` | `/`                     | Delete every secret in the environment. **Workspace owner only.**                                    |
| `GET`    | `/{secret_id}/versions` | Version history. Returns stored ciphertext, not plaintext. Paid capability.                          |
| `GET`    | `/{secret_id}/logs`     | Audit entries, newest first.                                                                         |
| `POST`   | `/share`                | Mint a share link. Paid capability — see [Share secrets](/guides/sharing).                           |

Keys must be valid environment-variable names, since the CLI writes them into a file a shell will `source`.

### What a read returns

```json theme={null}
{
  "id": "6f1c…",
  "key": "DATABASE_URL",
  "value": "postgres://…",
  "description": "Primary DB",
  "created_at": "2026-05-02T09:14:00Z",
  "updated_at": "2026-08-11T17:02:00Z",
  "current_version": 4,
  "environment_type": "prod",
  "last_rotated_at": "2026-08-11T17:02:00Z",
  "rotation_days": 90
}
```

`last_rotated_at` is `null` for secrets written before rotation tracking existed — fall back to `created_at`. `rotation_days` is the cadence governing this secret: its own policy if it has one, otherwise its environment's.

<Warning>
  A list can answer **`207 Multi-Status`** when some values could not be decrypted. The readable records are under `detail.succeeded` and the failures under `detail.failed_ids`. Treat `207` as a partial success, not an error — the CLI warns and carries on with what it got.
</Warning>

### Example

```bash theme={null}
base='https://usesink.co/secrets/<workspace>/<team>/<project>/<environment>'
key='sk-…'

curl -X POST "$base/bulk" \
  -H "X-API-Key: $key" \
  -H 'Content-Type: application/json' \
  -d '{"secrets":[
        {"key":"DATABASE_URL","value":"postgres://…"},
        {"key":"REDIS_URL","value":"redis://…"}
      ]}'

curl "$base/" -H "X-API-Key: $key"
```

## Invites

| Method | Path                       | Notes                                                                                    |
| ------ | -------------------------- | ---------------------------------------------------------------------------------------- |
| `POST` | `/invite/{workspace_id}`   | Send an invite. Owner only; counts against the seat limit.                               |
| `GET`  | `/invite/{workspace_id}`   | List a workspace's invites and their statuses.                                           |
| `POST` | `/invite/validate?token=…` | Check a token without side effects.                                                      |
| `POST` | `/invite/accept`           | `{ "token", "name", "password", "role", "team_id" }` — creates the account if it is new. |
| `POST` | `/invite/reject?token=…`   | Decline.                                                                                 |

Roles are integers: `0` Viewer, `1` Member, `2` Admin, `3` Owner. See [Access control](/security/access-control).
