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

# Saved logins and protected input

Save the website logins your tools sign in with, list them, change them and delete them from the Dashboard, the REST API or an MCP client. Enter website credentials in Pomerado's protected browser forms when you want to keep them out of prompts and MCP tool arguments.

A saved login belongs to the account. Authorized clients can use it according to their permissions. Its ID starts with `login_`, such as `login_3f0c8a5e1d2b4c6f8a9b0c1d2e3f4a5b`.

Reading logins needs the `logins:read` permission. Saving, changing, deleting and importing them needs `logins:manage`.

## Choose a saved login

List the logins with `GET /v1/logins` or, in an MCP client, `list_logins`. An optional `site_url` filters the results to one website: a host such as `example.com` or `www.example.com`, or a URL on it. Logins anywhere on the site match, so `example.com` also finds a login saved for `https://www.example.com`. Page through the results with `limit` and `cursor`.

```json theme={null}
{
  "site_url": "example.com"
}
```

The response has the logins in `data` and a `next_cursor` that is `null` on the last page. It contains metadata only. A list never contains a password or other secret.

```json theme={null}
{
  "data": [
    {
      "id": "login_3f0c8a5e1d2b4c6f8a9b0c1d2e3f4a5b",
      "label": "My example login",
      "site": { "url": "https://example.com", "name": "example.com" },
      "username_masked": "a***",
      "identifier_type": "username",
      "sign_in": "password",
      "saved_fields": ["email"],
      "two_factor": { "authenticator": false, "sms_number": "none" },
      "status": "verified",
      "needs_attention": null,
      "locked": false,
      "created_at": "2026-10-01T16:20:00.000Z",
      "updated_at": "2026-10-03T09:05:00.000Z",
      "verified_at": "2026-10-05T18:00:00.000Z"
    }
  ],
  "next_cursor": null
}
```

* `status` is `unverified` until a sign-in with the login succeeds, `verified` after that, and `needs_attention` when the website rejected it. Then `needs_attention.field` names the rejected field.
* `sign_in` is `password`, or `code` when the website sends a code at each sign-in and no password is saved.
* `saved_fields` and `two_factor.authenticator` are `null` when Pomerado has not recorded what the login holds.
* `locked` is `true` for a Personal login after its first verified sign-in.
* `created_at` and `updated_at` are when the login was saved and last changed, and `verified_at` is its first verified sign-in, or `null` before one. All are ISO 8601 UTC times.

`GET /v1/logins/{id}` returns one login.

A label is optional. A login without one has `"label": null`; refer to it by its site and masked username, as the Dashboard does: `example.org · jo***om`. A change keeps the label unless it sends a new `label`, or `"label": null` to remove it.

Pass the selected ID as `connected_account_id` to `run_website_tool` or `build_website_tool`, or in a run or build request. Follow each generated tool's advertised schema for its login selector.

* Choose the intended login before starting a write.
* When saving a login, give its website as a host (`example.com`), with `www.`, or as a pasted URL. Pomerado saves the site's HTTPS origin. Embedded credentials and other schemes are refused.
* Keep the login ID separate from the integration ID and job ID.

## Follow the account's login policy

| Account | Saved logins per site | Editing policy |
| - | - | - |
| Personal Free | One | Locked after the first verified sign-in |
| Personal Premium | One | Locked after the first verified sign-in |
| Business | Multiple | Editable with the required permissions |

* Correct an unverified Personal login before its first successful sign-in.
* Expect updates to be refused while the login is in active use.
* Treat the Personal login lock as permanent after verified sign-in.
* Treat deletion as credential removal, not a way to unlock or replace a verified Personal login.

Deleting a verified Personal login does not free its site slot. Premium uses the same login policy as Free.

## Save a login

`POST /v1/logins` saves a login and returns `201` with the new login. In an MCP client, call `call_pomerado_api` with the operation `logins.create`.

```json theme={null}
{
  "site_url": "example.com",
  "label": "My example login",
  "username": "jo@example.com",
  "password": "use-a-real-password"
}
```

* `site_url` is required. Send at least one of `username`, `email`, `phone` or `account_number`. The first in that order is the sign-in identifier, unless `primary_identifier` names another one you sent.
* A password login needs `password`. For a website that sends a code each time, send `"sign_in": "code"` and no password.
* Optional extras are `date_of_birth`, `zip`, `authenticator_secret`, `recovery_codes` and `preferred_method` (`sms`, `call`, `email`, `authenticator`, `push` or `recovery_code`).
* A Personal account can save one login per website. A second save for the same site is refused with `site_login_exists`.

Values you send travel through your client and, for an MCP client, its model provider. To type them on a Dashboard page instead, send `"deliver": "dashboard"` with only `site_url` and, if you like, `label`. The answer is `{"dashboard_url": "..."}`, a link to the page where you enter the login. Nothing is saved until you submit that page.

## Change a login

`PATCH /v1/logins/{id}` (`logins.update`) changes a saved login. Every field is optional: a field you leave out keeps its value, and `null` clears an optional one.

* A password login's label, identifiers and authenticator secret change only together with its `password`, since they are saved with it. Its other fields (`date_of_birth`, `zip`, `recovery_codes`, `preferred_method`) change on their own.
* The website and the sign-in mode never change. Save a new login for another site, or delete and save again to switch between password and code sign-in.
* Send `"deliver": "dashboard"` alone to get `{"dashboard_url": "..."}` for the login's edit page.
* A Personal login that has signed in successfully is locked and refuses changes.

## Replace recovery codes

`PUT /v1/logins/{id}/recovery-codes` (`logins.set_recovery_codes`) replaces the login's recovery codes.

```json theme={null}
{
  "codes": ["a1b2-c3d4", "e5f6-a7b8"]
}
```

It returns the login. Pomerado never returns a recovery code. The Dashboard shows only how many are saved and unused.

## Import many logins

`POST /v1/logins/import` (`logins.import`) takes up to 1000 logins, each with the fields a single save takes.

```json theme={null}
{
  "logins": [
    { "site_url": "example.com", "username": "jo", "password": "first-password" },
    { "site_url": "example.org", "username": "al", "password": "second-password" }
  ]
}
```

The answer is `{"results": [...]}`, one entry per login in order. Each has the `index` and either the saved `login` or an `error`, so one refused login does not stop the others. Send `"deliver": "dashboard"` instead of `logins` to get the link to the Dashboard's import page.

## Two-factor sign-in and Pomerado phone numbers

Business accounts can give a login a Pomerado phone number, so Pomerado reads the website's text-message sign-in codes for you. The number operations are:

| Request | What it does |
| - | - |
| `GET /v1/logins/{id}/sms-number` | Show the login's number |
| `POST /v1/logins/{id}/sms-number` | Assign a number |
| `POST /v1/logins/{id}/sms-number/verify` | Open a 10-minute verification and show the code of the newest text received |
| `DELETE /v1/logins/{id}/sms-number` | Unlink the number (`204`) |

Each answer is the number: its `phone_number`, `status` (`linked` or `verified`), `linked_at`, `verified_at` and, during a verification, `verification.expires_at`, `code_received` and `code`. Showing the number of a login that has none is `404 sms_number_not_linked`; an account without the feature gets `sms_number_not_allowed`. A login's `two_factor.sms_number` says `none` or `linked`.

An authenticator secret saved with the login lets Pomerado compute authenticator codes during sign-in. Recovery codes are used up as sign-ins need them.

## See and reveal secrets on the Dashboard

Showing a saved password, reading the current authenticator code and setting the Logins PIN happen only on the Dashboard. The REST API and MCP answer `{"dashboard_url": "..."}` for them instead, so that no password or PIN reaches a script or a model provider.

* Reveal a login's saved values from its Logins page. A reveal shows everything except the authenticator secret, and counts the recovery codes.
* The login's authenticator code page shows the code that is valid now.
* On a Personal account, both need the Logins PIN. You set, change, reset, unlock and lock it on the Logins page.
* A Business account needs a sign-in within the last five minutes.

## Delete a saved login

`DELETE /v1/logins/{id}` (`logins.delete`) deletes the login and answers `204`.

* Treat the login as revoked once deletion is requested.
* Expect related jobs and saved sessions to be stopped.
* Retry the deletion if a first attempt ends without an answer.
* Preserve the Personal site-slot restriction after verified login deletion.

Deletion removes saved access. It does not reverse actions already performed on the website.

## Use logins from an MCP client

The account MCP has one login tool of its own: `list_logins`, with `site_url`, `cursor` and `limit`. It returns the same `data` and `next_cursor` as the REST list. Every other login operation is reached with `call_pomerado_api`:

1. Call `search_pomerado_api` with the `logins` group to find the operations.
2. Call `describe_pomerado_api` with an operation, such as `logins.create`, to read its input.
3. Call `call_pomerado_api` with the operation and its input.

Operations that would reveal a secret answer `dashboard_url`. A client that supports URL elicitation asks you to open it.

## Supply credentials to a waiting job

When a job's login is missing, rejected by the website or expired, the job asks for it in place. `get_job` shows a pending request with one `credential` question. Its `reason` is `missing_credentials`, `invalid_credentials` or `credentials_expired`, its `fields` is `username_password` or `password`, and `allowSave` says whether the login may be saved.

* Open the request's `protected_input_path` to enter the login there, or call `answer_job` with `job_id` alone to get its URL.
* A rejected or expired login keeps its username; you replace only the password.
* The job signs in again on the same browser once you answer. If the website still rejects the login, the job fails.
* Without an answer the job ends with `failure_reason` set to `no_response`.

A login you send through `answer_job` is visible to your client and its model provider. Use the protected page to keep it out of the conversation.

## Handle website challenges

Codes, sign-in choices and native website dialogs arrive the same way, as questions in a pending request. Answer them on the protected page or with `answer_job`; see [jobs](jobs.md). A code sent through `answer_job` may be visible to your client or its model provider.

Passwords, authenticator secrets and durable tokens never belong in `answer_job`.

See [jobs and results](jobs.md) for resuming work, [authentication](authentication.md) for permissions and [REST API](rest-api.md) for API keys.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.