# Password sets

Make password sets, save the passwords a task signs in with, and archive or delete them.

A password set is a group of passwords a task can sign in with. A password is a secret a worker uses on a website or in an app that you allow: an API key, or an app's sign-in. You put a value in and a task uses it, and no route ever answers the value back. [Password sets](/concepts/password-sets) explains why, and who answers a first use.

Every worker has a set of its own, which holds the passwords saved on that worker, and the workspace has one shared set. You can make more. A task signs in with the sets named in its `password_set_ids`. Unless it names others, that is the worker's own set, then the shared set, and the first set that has a sign-in for an app is the one used. A set holds up to 256 passwords.

A password is one of three kinds, by its `auth.type`:

| `auth.type` | What it is | Where a task uses it |
| --- | --- | --- |
| `environment_variable` | A value a command uses under a name. The command sees a stand-in, and Little Worker puts the real value in each request to one of the password's websites. | In a request to one of its `allowed_hosts`, in the places `injection_location` allows. |
| `mcp_oauth` | An app's sign-in: an access token and, if you give it, the settings to refresh it. | In calls to the app at its `app_url`. |
| `static_bearer` | A fixed token for an app, such as an API key. | In calls to the app at its `app_url`, as `Authorization: Bearer`. |

An app sign-in goes in the shared set or a set you made, never in a worker's own set. A set holds one sign-in for each app.

A set can be personal. Make it with `owner` set to `me`, and it belongs to you: you and the workspace's owners see it, and only the tasks you start may name it. A routine cannot name it.

> [!WARNING]
> A password's value goes in and never comes out. No route returns it. No write on this page is kept for a retry, so every route ignores `Idempotency-Key`: a repeat of a save answers `409` for the taken name or app, and a repeat of Make a password set makes a second set.

> [!NOTE]
> A password set is a vault in Claude Managed Agents, and a password is a credential. A set holds up to 256 passwords where a vault holds 20, and a value is at least 8 characters where Claude's floor is 1.

## The password set object

The routes for sets answer a set, or a list of them. The routes for passwords answer a password, which has its own fields under [Save a password](#save-a-password). Times are RFC 3339 in UTC, to the millisecond.

- `type` (string): Always `password_set`.

- `id` (password set id): The set's id: `password_set_` and 32 hex digits.

- `display_name` (string): The set's name, 1 to 255 characters. Until someone renames it, a worker's own set has the worker's name, and the shared set is `Shared passwords`.

- `metadata` (object): Your own labels for the set: up to 16 keys of 1 to 64 characters, each with a string value of up to 512.

- `created_at` (time): When the set was made.

- `updated_at` (time): When the set last changed.

- `archived_at` (time, or null): When the set was archived. `null` while it is not.

- `owner` (user id, or null): The member a personal set belongs to, as their `user_` id. `null` for a set every member of the workspace shares. Only the owner and a workspace owner see a personal set.

```json
{
  "type": "password_set",
  "id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae",
  "display_name": "Billing",
  "metadata": {
    "team": "ops"
  },
  "created_at": "2026-10-20T09:14:02.331Z",
  "updated_at": "2026-10-20T09:14:02.331Z",
  "archived_at": null,
  "owner": null
}
```

## Make a password set

`POST /password_sets`

Make an empty set. It is shared by the workspace unless you make it personal with `owner`.

### Body

- `display_name` (string, required): The set's name, 1 to 255 characters.
- `metadata` (object): Your own labels for the set: up to 16 keys of 1 to 64 characters, each with a string value of up to 512.
- `owner` (string): `me`, or your own `user_` id, makes a personal set that belongs to you. Left out or `null`, the set is shared. A set cannot be made for another member, and its owner never changes.

### Returns

`200` and the set.

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/password_sets" \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19" \
  -H "content-type: application/json" \
  -d '{
  "display_name": "Billing",
  "metadata": {
    "team": "ops"
  }
}'
```

```json
{
  "type": "password_set",
  "id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae",
  "display_name": "Billing",
  "metadata": {
    "team": "ops"
  },
  "created_at": "2026-10-20T09:14:02.331Z",
  "updated_at": "2026-10-20T09:14:02.331Z",
  "archived_at": null,
  "owner": null
}
```

### Errors

- 400 `invalid_request_error`: A field is not valid (`error_code` `validation_error`): `display_name` is empty or over 255 characters, `metadata` breaks its limits, or `owner` is another member's id. A personal set belongs to the member who makes it.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## List password sets

`GET /password_sets`

List the sets you can see, newest first: the sets of the workspace that are not personal, every worker's own set, the shared set, and your personal sets. Other members' personal sets are left out, unless you are a workspace owner. Archived sets are left out unless you ask for them. The list ignores `order` and the `created_at` filters.

### Query parameters

- `limit` (integer): How many to answer, 1 to 100. The default is 20.
- `page` (string): The `next_page` of the answer before, to read the next page.
- `include_archived` (string): `true` lists archived sets too. One of `true`, `false`.

### Returns

A list: `data` holds the sets, and `next_page` is the `page` to send for the next ones (`null` on the last page).

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/password_sets?limit=10" \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "data": [
    {
      "type": "password_set",
      "id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae",
      "display_name": "Billing",
      "metadata": {
        "team": "ops"
      },
      "created_at": "2026-10-20T09:14:02.331Z",
      "updated_at": "2026-10-20T09:14:02.331Z",
      "archived_at": null,
      "owner": null
    }
  ],
  "next_page": null
}
```

### Errors

- 400 `invalid_request_error`: A filter is not valid, such as a `limit` outside 1 to 100 (`error_code` `validation_error`). `page` is not a page this list gave (`error_code` `invalid_cursor`). Read the first page again.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Read a password set

`GET /password_sets/{password_set_id}`

Read one set, archived or not.

### Path parameters

- `password_set_id` (password set id, required): The password set, such as `password_set_597a64d52e7f4e84ac5d95929bf4cbae`.

### Returns

The set.

### Example

```bash
curl https://core.thelittleworker.com/api/v1/password_sets/password_set_597a64d52e7f4e84ac5d95929bf4cbae \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "type": "password_set",
  "id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae",
  "display_name": "Billing",
  "metadata": {
    "team": "ops"
  },
  "created_at": "2026-10-20T09:14:02.331Z",
  "updated_at": "2026-10-20T09:14:02.331Z",
  "archived_at": null,
  "owner": null
}
```

### Errors

- 400 `invalid_request_error`: `password_set_id` is not a password set id. `error_code` is `validation_error`.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No set in the workspace has this id, or it is another member's personal set (`Password set not found.`). A workspace owner sees every member's personal set.
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Change a password set

`POST /password_sets/{password_set_id}`

Change a set's name or labels. A field left out stays as it was. An archived set takes no change.

### Path parameters

- `password_set_id` (password set id, required): The password set, such as `password_set_597a64d52e7f4e84ac5d95929bf4cbae`.

### Body

- `display_name` (string): A new name, 1 to 255 characters.
- `metadata` (object): Labels to set on the set. A key with a string value is set, a key set to `null` is removed, and keys left out stay. At most 16 keys remain. `null` instead of an object removes every label.

### Returns

The set, changed.

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/password_sets/password_set_597a64d52e7f4e84ac5d95929bf4cbae" \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19" \
  -H "content-type: application/json" \
  -d '{
  "display_name": "Billing tools"
}'
```

```json
{
  "type": "password_set",
  "id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae",
  "display_name": "Billing tools",
  "metadata": {
    "team": "ops"
  },
  "created_at": "2026-10-20T09:14:02.331Z",
  "updated_at": "2026-10-20T10:02:47.915Z",
  "archived_at": null,
  "owner": null
}
```

### Errors

- 400 `invalid_request_error`: A field is not valid (`error_code` `validation_error`): `password_set_id` is not a password set id, `display_name` is empty or over 255 characters, a label breaks its limits, or the labels would pass 16.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No set in the workspace has this id, or it is another member's personal set (`Password set not found.`). A workspace owner sees every member's personal set.
- 409 `conflict_error`: The set is archived and takes no change (`This password set is archived and cannot change.`).
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Archive a password set

`POST /password_sets/{password_set_id}/archive`

Archive a set. Every value it holds is wiped at once, and its passwords are archived with it. The records stay. It cannot be undone. Every enabled [routine](/api/routines) that names the set in its `password_set_ids` is paused. Archiving a set twice changes nothing.

### Path parameters

- `password_set_id` (password set id, required): The password set, such as `password_set_597a64d52e7f4e84ac5d95929bf4cbae`.

### Returns

The set, with `archived_at` set.

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/password_sets/password_set_597a64d52e7f4e84ac5d95929bf4cbae/archive" \
  -X POST \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "type": "password_set",
  "id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae",
  "display_name": "Billing",
  "metadata": {
    "team": "ops"
  },
  "created_at": "2026-10-20T09:14:02.331Z",
  "updated_at": "2026-10-25T17:12:03.418Z",
  "archived_at": "2026-10-25T17:12:03.418Z",
  "owner": null
}
```

### Errors

- 400 `invalid_request_error`: `password_set_id` is not a password set id. `error_code` is `validation_error`.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No set in the workspace has this id, or it is another member's personal set (`Password set not found.`). A workspace owner sees every member's personal set.
- 409 `conflict_error`: The set is a worker's own (`A worker's own password set cannot be archived; it goes with its worker.`), or the workspace's shared set (`The workspace's shared password set cannot be archived.`).
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Delete a password set

`DELETE /password_sets/{password_set_id}`

Delete a set and every password in it. It cannot be undone. Every enabled [routine](/api/routines) that names the set in its `password_set_ids` is paused, and the set leaves its list.

### Path parameters

- `password_set_id` (password set id, required): The password set, such as `password_set_597a64d52e7f4e84ac5d95929bf4cbae`.

### Returns

`type` is `password_set_deleted`, with the set's `id`.

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/password_sets/password_set_597a64d52e7f4e84ac5d95929bf4cbae" \
  -X DELETE \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "type": "password_set_deleted",
  "id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae"
}
```

### Errors

- 400 `invalid_request_error`: `password_set_id` is not a password set id. `error_code` is `validation_error`.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No set in the workspace has this id, or it is another member's personal set (`Password set not found.`). A workspace owner sees every member's personal set.
- 409 `conflict_error`: The set is a worker's own (`A worker's own password set cannot be deleted; it goes with its worker.`), or the workspace's shared set (`The workspace's shared password set cannot be deleted.`).
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Save a password

`POST /password_sets/{password_set_id}/passwords`

Save a password in a set. Its `auth` says what kind it is and holds the value. The value is sealed on arrival, and the answer never shows it.

A new password is `always` if your key was made with **Can allow actions**, and `ask` for any other key. Only a key that can allow actions may set `always`. The first use of an `ask` password in a task waits for a person's Allow.

A set holds up to 256 passwords. The count leaves out archived passwords, except in a worker's own set, where an archived password still counts and still holds its name.

### Path parameters

- `password_set_id` (password set id, required): The password set, such as `password_set_597a64d52e7f4e84ac5d95929bf4cbae`.

### Body

- `auth` (object, required): What to save. Send the fields of its `type`, as the table of kinds says.
  - `type` (string, required): `environment_variable` saves a value a command uses under a name. `mcp_oauth` saves an app sign-in that Little Worker refreshes. `static_bearer` saves a fixed token for an app, such as an API key. One of `environment_variable`, `mcp_oauth`, `static_bearer`.
  - `secret_name` (string): Required for `environment_variable`. The name a command uses: 1 to 255 letters, digits and `_`, not starting with a digit. A name the computer uses is refused, whatever its case: `PATH` and `https_proxy` are two, and so is any name starting `LD_`, `CURSOR_`, `CLOUD_AGENT_` or `SAND_`.
  - `secret_value` (string): Required for `environment_variable`. The value, 8 to 4,096 characters. It is sealed on arrival and no route answers it.
  - `networking` (object): Required for `environment_variable`. The websites the value may be sent to, as `{ "type": "limited", "allowed_hosts": [...] }`.
    - `type` (string, required): Always `limited`.
    - `allowed_hosts` (array of strings, required): 1 to 16 websites, each once: a lowercase name such as `api.example.com`, a `*.example.com` wildcard that covers its subdomains but not `example.com` itself, or a public IPv4 address. Private and special addresses are refused, and so is a wildcard over a shared host. A value goes to an IPv4 address only when the website's certificate lists it.
  - `injection_location` (object): For `environment_variable`. Where the real value may go in a request. `header` and `body` are each on or off. A field left out is off, and both off is refused. Left out whole, it is the header only.
    - `header` (boolean): Whether the value may go in a request's headers.
    - `body` (boolean): Whether the value may go in a request's body and address.
  - `app_url` (string): Required for `mcp_oauth` and `static_bearer`. The address of the app, an `https` address of up to 2,048 characters. It cannot change later.
  - `token` (string): Required for `static_bearer`. The fixed token, 1 to 8,192 characters. Little Worker sends it to the app as `Authorization: Bearer`.
  - `access_token` (string): Required for `mcp_oauth`. The access token, 1 to 16,384 characters.
  - `expires_at` (time): For `mcp_oauth`. When the access token expires, as an RFC 3339 time in UTC ending in `Z`. Little Worker refreshes the token when it is close. Left out or `null`, it uses the token as it is and never refreshes it.
  - `refresh` (object): For `mcp_oauth`. How Little Worker gets a new access token. Without it, the sign-in works until the token expires. `null` is the same as leaving it out.
    - `client_id` (string, required): The client that signs in at the token endpoint, 1 to 16,384 characters. It cannot change later.
    - `refresh_token` (string, required): The refresh token, 1 to 16,384 characters.
    - `token_endpoint` (string, required): The `https` address where Little Worker asks for a new access token, up to 2,048 characters. It cannot change later.
    - `token_endpoint_auth` (object, required): How Little Worker signs in at the token endpoint: `{ "type": "none" }`, or `{ "type": "client_secret_basic", "client_secret" }`, or `{ "type": "client_secret_post", "client_secret" }`. A client secret is 1 to 16,384 characters.
    - `resource` (string): The resource the token is for, up to 2,048 characters.
    - `scope` (string): The scope the token is for, up to 2,048 characters.
- `display_name` (string): A name for the password, up to 255 characters. Left out or `null`, the password has none.
- `metadata` (object): Your own labels for the password: up to 16 keys of 1 to 64 characters, each with a string value of up to 512.
- `permission` (string): `always` uses the password without asking. `ask` waits for a person's Allow on its first use in a task. Left out, it is `always` for a key that can allow actions, and `ask` for any other key. One of `ask`, `always`.

### Returns

`200` and the password. It never holds a value.

- `type` (string): Always `password`.
- `id` (password id): The password's id: `password_` and 32 hex digits.
- `password_set_id` (password set id): The set the password is in.
- `display_name` (string, or null): The password's name as a person reads it, up to 255 characters. `null` when it has none.
- `metadata` (object): Your own labels for the password: up to 16 keys of 1 to 64 characters, each with a string value of up to 512.
- `auth` (object): What the password is and where it may go. A value, a token or a client secret is never in it. Its `type` decides the other fields.
  - `type` (string): `environment_variable` is a value a command uses under a name. `mcp_oauth` is an app sign-in. `static_bearer` is a fixed token for an app. One of `environment_variable`, `mcp_oauth`, `static_bearer`.
  - `secret_name` (string): Only on `environment_variable`: the name a command uses.
  - `networking` (object): Only on `environment_variable`: the websites the value may be sent to, as `{ "type": "limited", "allowed_hosts": [...] }`.
  - `injection_location` (object): Only on `environment_variable`: where the value may go in a request, as `{ "header": true, "body": false }`.
  - `app_url` (string): Only on `mcp_oauth` and `static_bearer`: the address of the app the sign-in is for, in its plain form: a lowercase scheme and host, no default port and no trailing slash.
  - `expires_at` (time, or null): Only on `mcp_oauth`: when the access token expires. `null` when it does not say.
  - `refresh` (object, or null): Only on `mcp_oauth`: how Little Worker gets a new access token, without the refresh token and the client secret. `null` when the sign-in has no refresh settings.
    - `client_id` (string): The client that signs in at the token endpoint.
    - `token_endpoint` (string): The address Little Worker asks for a new access token.
    - `token_endpoint_auth` (object): How Little Worker signs in at the token endpoint: `{ "type" }` is `none`, `client_secret_basic` or `client_secret_post`.
    - `resource` (string, or null): The resource the token is for, if one was given.
    - `scope` (string, or null): The scope the token is for, if one was given.
- `permission` (string): `always` lets a task use the password without asking. `ask` waits for a person's Allow the first time a task uses it (see [Approvals](/concepts/approvals)). One of `ask`, `always`.
- `created_at` (time): When the password was saved.
- `updated_at` (time): When the password last changed.
- `archived_at` (time, or null): When the password was archived, and its value wiped. `null` while it is not.

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/password_sets/password_set_597a64d52e7f4e84ac5d95929bf4cbae/passwords" \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19" \
  -H "content-type: application/json" \
  -d '{
  "display_name": "Billing API key",
  "auth": {
    "type": "environment_variable",
    "secret_name": "BILLING_API_KEY",
    "secret_value": "replace-with-the-real-key",
    "networking": {
      "type": "limited",
      "allowed_hosts": [
        "api.billing.example.com"
      ]
    }
  },
  "permission": "ask"
}'
```

```json
{
  "type": "password",
  "id": "password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46",
  "password_set_id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae",
  "display_name": "Billing API key",
  "metadata": {},
  "auth": {
    "type": "environment_variable",
    "secret_name": "BILLING_API_KEY",
    "networking": {
      "type": "limited",
      "allowed_hosts": [
        "api.billing.example.com"
      ]
    },
    "injection_location": {
      "header": true,
      "body": false
    }
  },
  "permission": "ask",
  "created_at": "2026-10-20T09:31:55.208Z",
  "updated_at": "2026-10-20T09:31:55.208Z",
  "archived_at": null
}
```

### Errors

- 400 `invalid_request_error`: A field is not valid (`error_code` `validation_error`): a name, value, website, address or label that breaks its rule, a field its `type` needs is missing or one it does not take is sent, both `injection_location` fields off, the set already holds 256 passwords, or an app sign-in is saved in a worker's own set (`message` says which).
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 403 `permission_error`: `permission` is `always`, and the key was not made with **Can allow actions** (`error_code` `person_required`). Make a key with **Can allow actions** on.
- 404 `not_found_error`: No set in the workspace has this id, or it is another member's personal set (`Password set not found.`). A workspace owner sees every member's personal set.
- 409 `conflict_error`: The set is archived (`This password set is archived; it takes no new passwords.`), already has a password with this `secret_name`, or already has a sign-in for this `app_url` (the address in its plain form). Pick another name, or change the password that is there.
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## List passwords

`GET /password_sets/{password_set_id}/passwords`

List the passwords in a set, newest first. Archived passwords are left out unless you ask for them. The list ignores `order` and the `created_at` filters. An app sign-in made in the app, which keeps its tokens there, is not listed.

### Path parameters

- `password_set_id` (password set id, required): The password set, such as `password_set_597a64d52e7f4e84ac5d95929bf4cbae`.

### Query parameters

- `limit` (integer): How many to answer, 1 to 100. The default is 20.
- `page` (string): The `next_page` of the answer before, to read the next page.
- `include_archived` (string): `true` lists archived passwords too. One of `true`, `false`.

### Returns

A list: `data` holds the passwords, as [Save a password](#save-a-password) shows them, and `next_page` is the `page` to send for the next ones (`null` on the last page).

### Example

```bash
curl https://core.thelittleworker.com/api/v1/password_sets/password_set_597a64d52e7f4e84ac5d95929bf4cbae/passwords \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "data": [
    {
      "type": "password",
      "id": "password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46",
      "password_set_id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae",
      "display_name": "Billing API key",
      "metadata": {},
      "auth": {
        "type": "environment_variable",
        "secret_name": "BILLING_API_KEY",
        "networking": {
          "type": "limited",
          "allowed_hosts": [
            "api.billing.example.com"
          ]
        },
        "injection_location": {
          "header": true,
          "body": false
        }
      },
      "permission": "ask",
      "created_at": "2026-10-20T09:31:55.208Z",
      "updated_at": "2026-10-20T09:31:55.208Z",
      "archived_at": null
    }
  ],
  "next_page": null
}
```

### Errors

- 400 `invalid_request_error`: A filter is not valid, such as a `limit` outside 1 to 100 (`error_code` `validation_error`). `page` is not a page this list gave (`error_code` `invalid_cursor`). Read the first page again.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No set in the workspace has this id, or it is another member's personal set (`Password set not found.`). A workspace owner sees every member's personal set.
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Read a password

`GET /password_sets/{password_set_id}/passwords/{password_id}`

Read one password. The answer shows where it may go, never its value.

### Path parameters

- `password_set_id` (password set id, required): The password set, such as `password_set_597a64d52e7f4e84ac5d95929bf4cbae`.
- `password_id` (password id, required): The password, such as `password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46`.

### Returns

The password, as [Save a password](#save-a-password) shows it.

### Example

```bash
curl https://core.thelittleworker.com/api/v1/password_sets/password_set_597a64d52e7f4e84ac5d95929bf4cbae/passwords/password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46 \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "type": "password",
  "id": "password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46",
  "password_set_id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae",
  "display_name": "Billing API key",
  "metadata": {},
  "auth": {
    "type": "environment_variable",
    "secret_name": "BILLING_API_KEY",
    "networking": {
      "type": "limited",
      "allowed_hosts": [
        "api.billing.example.com"
      ]
    },
    "injection_location": {
      "header": true,
      "body": false
    }
  },
  "permission": "ask",
  "created_at": "2026-10-20T09:31:55.208Z",
  "updated_at": "2026-10-20T09:31:55.208Z",
  "archived_at": null
}
```

### Errors

- 400 `invalid_request_error`: `password_set_id` or `password_id` is not an id of its kind. `error_code` is `validation_error`.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No set you can see has this `password_set_id` (`Password set not found.`), or the set has no password with this `password_id` (`Password not found.`).
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Change a password

`POST /password_sets/{password_set_id}/passwords/{password_id}`

Change a password's value, where it may go, its name, its labels or its permission. A field left out stays as it was. Send `auth` with the password's own `type`. A task at work reads an `environment_variable` password for each command, so its next command uses the change.

A change through the API to the websites or the places of an `environment_variable` password sets its `permission` back to `ask`, so its next use waits for a person. Send `permission` in the same change to set it again, which takes a key that can allow actions. Only a person, in the app, may loosen a password saved in the app.

### Path parameters

- `password_set_id` (password set id, required): The password set, such as `password_set_597a64d52e7f4e84ac5d95929bf4cbae`.
- `password_id` (password id, required): The password, such as `password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46`.

### Body

- `auth` (object): The changes to the value and where it goes. Its `type` must be the password's own.
  - `type` (string, required): The password's own `auth.type`. It cannot change, and neither can `secret_name`. One of `environment_variable`, `mcp_oauth`, `static_bearer`.
  - `secret_value` (string): For `environment_variable`. A new value, 8 to 4,096 characters.
  - `networking` (object): For `environment_variable`. A new `{ "type": "limited", "allowed_hosts": [...] }`, which replaces the old websites. `allowed_hosts` holds 1 to 16 websites, each once: a lowercase name such as `api.example.com`, a `*.example.com` wildcard that covers its subdomains but not `example.com` itself, or a public IPv4 address. Private and special addresses are refused, and so is a wildcard over a shared host. A value goes to an IPv4 address only when the website's certificate lists it.
  - `injection_location` (object): For `environment_variable`. Where the real value may go in a request. Send `header`, `body` or both: each field you send replaces that one, and a field you leave out stays. Both off is refused.
  - `token` (string): For `static_bearer`. A new token, 1 to 8,192 characters. `app_url` cannot change.
  - `access_token` (string): For `mcp_oauth`. A new access token, 1 to 16,384 characters.
  - `expires_at` (time): For `mcp_oauth`. When the access token expires, as an RFC 3339 time in UTC ending in `Z`. Left out, the old time stays. `null` removes it.
  - `refresh` (object): For `mcp_oauth`. Changes to the refresh settings: a new `refresh_token`, a new `scope` (`null` removes it), and a new `token_endpoint_auth` of `client_secret_basic` or `client_secret_post`, with a new `client_secret` unless the sign-in already holds one. `null` removes the refresh settings with their refresh token and client secret. A sign-in saved with no refresh settings cannot get them in a change: the call is refused. `client_id`, `token_endpoint` and `resource` cannot change, and a sign-in cannot move to `none`.
- `display_name` (string): A new name, up to 255 characters. `null` removes the name.
- `metadata` (object): Labels to set on the password. A key with a string value is set, a key set to `null` is removed, and keys left out stay. At most 16 keys remain. `null` instead of an object removes every label.
- `permission` (string): `ask` or `always`. Any key may set `ask`. `always` takes a key made with **Can allow actions**. One of `ask`, `always`.

### Returns

The password, as [Save a password](#save-a-password) shows it.

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/password_sets/password_set_597a64d52e7f4e84ac5d95929bf4cbae/passwords/password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46" \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19" \
  -H "content-type: application/json" \
  -d '{
  "auth": {
    "type": "environment_variable",
    "secret_value": "replace-with-the-new-key"
  }
}'
```

```json
{
  "type": "password",
  "id": "password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46",
  "password_set_id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae",
  "display_name": "Billing API key",
  "metadata": {},
  "auth": {
    "type": "environment_variable",
    "secret_name": "BILLING_API_KEY",
    "networking": {
      "type": "limited",
      "allowed_hosts": [
        "api.billing.example.com"
      ]
    },
    "injection_location": {
      "header": true,
      "body": false
    }
  },
  "permission": "ask",
  "created_at": "2026-10-20T09:31:55.208Z",
  "updated_at": "2026-10-20T11:40:09.562Z",
  "archived_at": null
}
```

### Errors

- 400 `invalid_request_error`: A field is not valid (`error_code` `validation_error`): an id is not of its kind, `auth.type` is not the password's own, a value, website or label breaks its rule, both `injection_location` fields would be off, the labels would pass 16, a `refresh` is sent for a sign-in with no refresh settings, or a new `token_endpoint_auth` has no `client_secret` and the sign-in holds none (`message` says which).
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 403 `permission_error`: The change sets `permission` to `always` where it is not `always` now (`error_code` `person_required`). Either the key was not made with **Can allow actions**: make a key with it on. Or the password was saved in the app: only a person can loosen it, in the app, whatever the key.
- 404 `not_found_error`: No set you can see has this `password_set_id` (`Password set not found.`), or the set has no password with this `password_id` (`Password not found.`).
- 409 `conflict_error`: The password is archived and cannot change (`This password is archived and cannot change.`). Every password of an archived set is archived. Or an `mcp_oauth` sign-in's tokens cannot be read (`This sign-in's tokens cannot be read; delete it and save it again.`).
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Archive a password

`POST /password_sets/{password_set_id}/passwords/{password_id}/archive`

Archive a password. Its value is wiped at once and its record stays, so a task cannot use it again. No route brings it back. Archiving a password twice changes nothing.

### Path parameters

- `password_set_id` (password set id, required): The password set, such as `password_set_597a64d52e7f4e84ac5d95929bf4cbae`.
- `password_id` (password id, required): The password, such as `password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46`.

### Returns

The password, as [Save a password](#save-a-password) shows it, with `archived_at` set.

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/password_sets/password_set_597a64d52e7f4e84ac5d95929bf4cbae/passwords/password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46/archive" \
  -X POST \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "type": "password",
  "id": "password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46",
  "password_set_id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae",
  "display_name": "Billing API key",
  "metadata": {},
  "auth": {
    "type": "environment_variable",
    "secret_name": "BILLING_API_KEY",
    "networking": {
      "type": "limited",
      "allowed_hosts": [
        "api.billing.example.com"
      ]
    },
    "injection_location": {
      "header": true,
      "body": false
    }
  },
  "permission": "ask",
  "created_at": "2026-10-20T09:31:55.208Z",
  "updated_at": "2026-10-25T17:12:03.418Z",
  "archived_at": "2026-10-25T17:12:03.418Z"
}
```

### Errors

- 400 `invalid_request_error`: `password_set_id` or `password_id` is not an id of its kind. `error_code` is `validation_error`.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No set you can see has this `password_set_id` (`Password set not found.`), or the set has no password with this `password_id` (`Password not found.`).
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Delete a password

`DELETE /password_sets/{password_set_id}/passwords/{password_id}`

Delete a password and its record. A task cannot use it again.

### Path parameters

- `password_set_id` (password set id, required): The password set, such as `password_set_597a64d52e7f4e84ac5d95929bf4cbae`.
- `password_id` (password id, required): The password, such as `password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46`.

### Returns

`type` is `password_deleted`, with the password's `id`.

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/password_sets/password_set_597a64d52e7f4e84ac5d95929bf4cbae/passwords/password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46" \
  -X DELETE \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "type": "password_deleted",
  "id": "password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46"
}
```

### Errors

- 400 `invalid_request_error`: `password_set_id` or `password_id` is not an id of its kind. `error_code` is `validation_error`.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No set you can see has this `password_set_id` (`Password set not found.`), or the set has no password with this `password_id` (`Password not found.`).
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Check an app sign-in

`POST /password_sets/{password_set_id}/passwords/{password_id}/mcp_oauth_validate`

Check that an `mcp_oauth` sign-in still works. Little Worker sends the app an MCP `initialize` call with the token. If the app refuses it (`401` or `403`), it tries the refresh token once, keeps the new tokens if that works, and sends the call again. The answer says how it went, and keeps the app's answers with every secret masked. A `static_bearer` or `environment_variable` password cannot be checked.

### Path parameters

- `password_set_id` (password set id, required): The password set, such as `password_set_597a64d52e7f4e84ac5d95929bf4cbae`.
- `password_id` (password id, required): The password, such as `password_0e4b8d2f6a1c4e7a9b3d5f7c1a2e8b46`.

### Returns

`type` is `password_validation`, with these fields.

- `type` (string): Always `password_validation`.
- `password_id` (password id): The password that was checked.
- `password_set_id` (password set id): Its set.
- `status` (string): `valid` when the app took a token, after a refresh if it needed one. `invalid` when the app refused the token with `401` or `403` and no refresh fixed it. `unknown` when the app or the token endpoint could not be reached, or answered something else. One of `valid`, `invalid`, `unknown`.
- `has_refresh_token` (boolean): Whether the sign-in has a refresh token and the settings to use it.
- `validated_at` (time): When the check ran.
- `mcp_probe` (object, or null): The failing step: the app's `initialize` call and the answer kept. `null` when the app took the token.
  - `method` (string): The MCP method Little Worker called.
  - `http_response` (object, or null): The app's answer. `null` when the app was not reached.
    - `status_code` (integer): The HTTP status the server answered.
    - `content_type` (string): The answer's content type.
    - `body` (string): The first 2,048 characters of the answer's body, with every secret of the sign-in masked.
    - `body_truncated` (boolean): `true` when the body is not the whole answer.
- `refresh` (object, or null): What came of the refresh Little Worker made after the app refused the token with `401` or `403`. `null` when the app did not refuse it so.
  - `status` (string): How the refresh went. `succeeded`: the token endpoint gave a new token. `failed`: it answered and refused. `connect_error`: it could not be reached or gave no token. `no_refresh_token`: the sign-in has no refresh token or no refresh settings, so none was tried. One of `succeeded`, `failed`, `connect_error`, `no_refresh_token`.
  - `http_response` (object, or null): The token endpoint's answer. `null` unless it answered and refused.
    - `status_code` (integer): The HTTP status the server answered.
    - `content_type` (string): The answer's content type.
    - `body` (string): The first 2,048 characters of the answer's body, with every secret of the sign-in masked.
    - `body_truncated` (boolean): `true` when the body is not the whole answer.

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/password_sets/password_set_597a64d52e7f4e84ac5d95929bf4cbae/passwords/password_6c2a8e4f0b1d4a7e9c3b5d7f1a4e2c80/mcp_oauth_validate" \
  -X POST \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "type": "password_validation",
  "password_id": "password_6c2a8e4f0b1d4a7e9c3b5d7f1a4e2c80",
  "password_set_id": "password_set_597a64d52e7f4e84ac5d95929bf4cbae",
  "status": "valid",
  "has_refresh_token": true,
  "validated_at": "2026-10-25T17:20:41.073Z",
  "mcp_probe": null,
  "refresh": null
}
```

### Errors

- 400 `invalid_request_error`: The password is not an `mcp_oauth` sign-in (`Only an mcp_oauth password can be checked.`), or an id is not of its kind (`error_code` `validation_error`).
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No set you can see has this `password_set_id` (`Password set not found.`), or the set has no password with this `password_id` (`Password not found.`).
- 409 `conflict_error`: The password is archived (`This password is archived and cannot change.`), or its tokens cannot be read (`This sign-in's tokens cannot be read; delete it and save it again.`).
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.
