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

# Credentials, logins and 2FA

> Store passwords, credit cards and secrets, use them in agents and from code, connect your own vault, and let Skyvern complete two-factor authentication with authenticator apps, email, SMS and magic links.

Save a login once and every agent can use it. Skyvern fills the username, password and 2FA code into the page for you.

The model that drives the browser gets placeholders, not your values. Skyvern swaps in the real value only when it types into the field. A typed value can still show up in later screenshots and page text, so turn on **Mask Secrets** in an agent's settings to hide it there too. Saved passwords, card numbers and secrets are never shown again in the app or returned by the API.

## Store a credential

<Tabs>
  <Tab title="In the app">
    <Steps>
      <Step title="Open Credentials">
        Click **Credentials** in the left sidebar. Its sub-items open the **Passwords**, **Credit Cards**, **Secrets**, **2FA** and **Magic Links** tabs.

        <img src="https://mintcdn.com/skyvern/amcBKP0IBwdNswdN/images/cloud/credentials-sidebar-nav.png?fit=max&auto=format&n=amcBKP0IBwdNswdN&q=85&s=ab1e304dcd2da5c0dcaefabff569c9f9" alt="Credentials in the sidebar with Passwords, Credit Cards, Secrets, 2FA and Magic Links" width="500" height="384" data-path="images/cloud/credentials-sidebar-nav.png" />
      </Step>

      <Step title="Click Add">
        Choose **Password**, **Credit Card** or **Secret**.

        <img src="https://mintcdn.com/skyvern/amcBKP0IBwdNswdN/images/cloud/credentials-add-dropdown.png?fit=max&auto=format&n=amcBKP0IBwdNswdN&q=85&s=846ece6c7332a79e2670d5ff4c18366b" alt="Add menu with Password, Credit Card and Secret" width="600" height="326" data-path="images/cloud/credentials-add-dropdown.png" />
      </Step>

      <Step title="Fill in the form and click Save">
        The credential is available to agents right away.
      </Step>
    </Steps>

    <img src="https://mintcdn.com/skyvern/amcBKP0IBwdNswdN/images/cloud/credentials-overview.png?fit=max&auto=format&n=amcBKP0IBwdNswdN&q=85&s=9b84ce657a48a8b8a81f9a5d48585a72" alt="Credentials page with Folders and the Passwords, Credit Cards, Secrets, 2FA and Magic Links tabs" width="1677" height="873" data-path="images/cloud/credentials-overview.png" />

    **Password** fields:

    * **Name**: a label you will recognize, such as "Vendor portal".
    * **Login Page URL**: where the sign-in form lives.
    * **Username or Email** and **Password**.
    * **Save browser session for future logins**: Skyvern signs in once, checks it worked, and saves the signed-in browser so later runs skip the login form. Needs the **Login Page URL**. You can add **Login instructions (optional)**, such as "Click the SSO button first".
    * **Two-Factor Authentication**: see [Two-factor authentication](#two-factor-authentication).
    * **Use a consistent IP address**: signs in from the same residential IP each time, which avoids extra 2FA prompts, captchas and lockouts caused by a new location.

    <img src="https://mintcdn.com/skyvern/amcBKP0IBwdNswdN/images/cloud/credentials-add-password.png?fit=max&auto=format&n=amcBKP0IBwdNswdN&q=85&s=f2efdba1eab96a2a9f2e47be4deae94b" alt="Add Credential dialog for a password" width="1204" height="991" data-path="images/cloud/credentials-add-password.png" />

    **Credit Card** fields: **Name**, **Cardholder Name**, **Number**, **Brand**, **Expiration** (`MM/YY`) and **CVV**. **Billing Details** (address, **Billing Email**, **Billing Phone**) are optional; fill them in when checkout pages ask for a billing address. After you save, only the last four digits are shown.

    <img src="https://mintcdn.com/skyvern/amcBKP0IBwdNswdN/images/cloud/credentials-add-credit-card.png?fit=max&auto=format&n=amcBKP0IBwdNswdN&q=85&s=0563a944a6fc19f16d412fd7026eca7a" alt="Add Credential dialog for a credit card, with card fields and Billing Details" width="966" height="1232" data-path="images/cloud/credentials-add-credit-card.png" />

    **Secret** fields: **Name**, **Secret Label (optional)** and **Secret Value**. Use secrets for API keys and tokens.

    Group credentials with **+ New folder**. Click a folder card to filter the list.

    To edit a credential, click the pencil icon on its row. Saved values are never shown again. The name, username and 2FA method stay filled in; leave the password blank to keep it, and re-enter the other secret values, such as the authenticator key or card number. The edit dialog also has **Run workflows sequentially for this credential**, which lets only one run use the account at a time.

    Click the trash icon to delete a credential. Deletion is permanent, and agents that use it fail until you pick another one.
  </Tab>

  <Tab title="With code">
    <CodeGroup>
      ```python Python theme={null}
      import asyncio
      import os

      from skyvern import Skyvern


      async def main():
          client = Skyvern(api_key=os.environ["SKYVERN_API_KEY"])
          credential = await client.create_credential(
              name="Vendor portal",
              credential_type="password",
              credential={
                  "username": "you@example.com",
                  "password": "your-password",
              },
          )
          print(credential.credential_id)  # cred_...


      asyncio.run(main())
      ```

      ```typescript TypeScript theme={null}
      import { Skyvern } from "@skyvern/client";

      const client = new Skyvern({ apiKey: process.env.SKYVERN_API_KEY! });

      const credential = await client.createCredential({
        name: "Vendor portal",
        credential_type: "password",
        credential: {
          username: "you@example.com",
          password: "your-password",
        },
      });
      console.log(credential.credential_id); // cred_...
      ```

      ```bash cURL theme={null}
      curl -X POST "https://api.skyvern.com/v1/credentials" \
        -H "x-api-key: $SKYVERN_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "name": "Vendor portal",
          "credential_type": "password",
          "credential": {"username": "you@example.com", "password": "your-password"}
        }'
      ```
    </CodeGroup>

    The `credential` object depends on `credential_type`:

    | `credential_type` | Fields in `credential` |
    | - | - |
    | `password` | `username`, `password`, optional `totp` (authenticator key) and `totp_type` (`authenticator`, `email`, `text`) |
    | `credit_card` | `card_holder_name`, `card_number`, `card_exp_month`, `card_exp_year`, `card_cvv`, `card_brand` (free text, such as `visa`) |
    | `secret` | `secret_value`, optional `secret_label` |

    List credentials with `get_credentials()` / `getCredentials()` and delete one with `delete_credential(credential_id)` / `deleteCredential(credentialId)`. Reads return names and metadata only, never the stored values. Full parameters are in the [API reference](/docs/api-reference) and [SDK reference](/docs/sdk-reference/overview).
  </Tab>
</Tabs>

## Use a credential in an agent

<Tabs>
  <Tab title="In the app">
    * **Login block**: in [Studio](/docs/cloud/studio), add a Login block and pick a saved login in its **Credential** field. Skyvern enters the username, password and 2FA code.
    * **Credential input**: to choose the account per run, open **Inputs**, click **Add Input** and set the value type to **credential**. Pick the credential type (**Password**, **Secret** or **Credit Card**) and the source (**Skyvern**, or a [connected vault](#use-your-own-vault)). Whoever runs the agent picks the credential from a dropdown.
    * **Credit cards** need no field mapping. A Browser Task or Browser Action block on a checkout page fills the card number, name, expiration and CVV from the credential input.
    * **Secrets** go where a block takes text, such as an HTTP Request or Code block. Reference the input's key: `{{ my_secret.secret_value }}`.

    See the [blocks reference](/docs/reference/blocks) for every Login block setting.
  </Tab>

  <Tab title="With code">
    Pass a credential ID as the value of an agent's credential input:

    <CodeGroup>
      ```python Python theme={null}
      run = await client.run_workflow(
          workflow_id="wpid_123",
          parameters={"login": "cred_123"},
          wait_for_completion=True,
      )
      ```

      ```typescript TypeScript theme={null}
      const run = await client.runWorkflow({
        agent_id: "wpid_123",
        parameters: { login: "cred_123" },
        waitForCompletion: true,
      });
      ```
    </CodeGroup>

    Or sign in without an agent. `login` opens the page, signs in and handles 2FA:

    <CodeGroup>
      ```python Python theme={null}
      run = await client.login(
          credential_type="skyvern",
          url="https://portal.example.com/login",
          credential_id="cred_123",
          prompt="After signing in, open the dashboard",
          wait_for_completion=True,
      )
      ```

      ```typescript TypeScript theme={null}
      const run = await client.login({
        credential_type: "skyvern",
        url: "https://portal.example.com/login",
        credential_id: "cred_123",
        prompt: "After signing in, open the dashboard",
        waitForCompletion: true,
      });
      ```

      ```bash cURL theme={null}
      curl -X POST "https://api.skyvern.com/v1/run/tasks/login" \
        -H "x-api-key: $SKYVERN_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "credential_type": "skyvern",
          "url": "https://portal.example.com/login",
          "credential_id": "cred_123"
        }'
      ```
    </CodeGroup>

    `credential_type` says where the login lives:

    | Value | Pass |
    | - | - |
    | `skyvern` | `credential_id` |
    | `bitwarden` | `bitwarden_collection_id`, `bitwarden_item_id` |
    | `1password` | `onepassword_vault_id`, `onepassword_item_id` |
    | `azure_vault` | `azure_vault_name`, `azure_vault_username_key`, `azure_vault_password_key`, optional `azure_vault_totp_secret_key` |

    To continue in the signed-in browser, pass `browser_session_id` (see [Browsers](/docs/browsers)). Coding agents connected through [MCP](/docs/developers/getting-started/mcp) sign in with the `skyvern_login` tool, so passwords never pass through the chat.
  </Tab>
</Tabs>

## Use your own vault

If your team already keeps logins in a vault, connect it and Skyvern reads items at run time. On [Integrations](/docs/cloud/integrations), connect one of the **Credentials** tiles:

| Vault | What you connect with | Credential types |
| - | - | - |
| **1Password** | A **Service Account Token** with access to the vault | Password, Credit Card |
| **Bitwarden** | Your Bitwarden account **Email** and **Master Password** | Password, Secret, Credit Card |
| **Azure Key Vault** | **Tenant ID**, **Client ID**, **Client Secret** | Password |
| **Custom Credential Service** | **API Base URL**, **API Token** of your own HTTP service | Password, Secret, Credit Card |

Then add a credential input and choose the vault as its source. The input asks for the item to use: a Bitwarden collection ID and item ID (or a URL parameter key to match by site), a 1Password vault ID and item ID, or an Azure vault name with the names of the secrets that hold the username, password and optional authenticator key. Items from connected 1Password and Bitwarden vaults also show on the **Passwords** tab.

<Note>
  1Password does not expose a card's native expiration field. For credit cards stored in 1Password, add a text field named **Expire Date**, **Expiry Date** or **Expiration Date** in `MM/YYYY` or `MM/YY` format.
</Note>

<Accordion title="Custom Credential Service: the API your service implements">
  With a custom service connected, the Add dialog shows **Store in Custom Credential Service**. Skyvern then calls your API with `Authorization: Bearer <API Token>`:

  | Request | Purpose | Your response |
  | - | - | - |
  | `POST {API Base URL}` | Create. Body has `name`, `type` (`password`, `credit_card` or `secret`) and the fields for that type, as in the table under [Store a credential](#store-a-credential). | `{"id": "..."}` |
  | `GET {API Base URL}/{id}` | Read at run time | The stored fields, including `type` |
  | `DELETE {API Base URL}/{id}` | Delete | Any 2xx status |

  Self-hosted Skyvern reads the same settings from `CREDENTIAL_VAULT_TYPE=custom`, `CUSTOM_CREDENTIAL_API_BASE_URL` and `CUSTOM_CREDENTIAL_API_TOKEN`. The other vaults (`CREDENTIAL_VAULT_TYPE` values `skyvern`, `bitwarden`, `azure_vault`, `gcp`), including a self-hosted [Bitwarden or Vaultwarden](/docs/reference/configuration#bitwarden-and-vaultwarden) setup, are in the [configuration reference](/docs/reference/configuration#credentials-and-encryption).
</Accordion>

## Two-factor authentication

Set 2FA up on the password credential itself: expand **Two-Factor Authentication** in the Add or Edit dialog and pick how the site sends codes.

<img src="https://mintcdn.com/skyvern/amcBKP0IBwdNswdN/images/cloud/credentials-2fa-setup.png?fit=max&auto=format&n=amcBKP0IBwdNswdN&q=85&s=f6b59afa54ae9f0a61c6353ef1f66e65" alt="Two-Factor Authentication options: Authenticator App, Email, Text Message and Passkey (Enterprise)" width="1366" height="458" data-path="images/cloud/credentials-2fa-setup.png" />

| Method | How Skyvern gets the code |
| - | - |
| **Authenticator App** | Generates it from the stored key. No waiting, no setup outside Skyvern. Use this whenever the site offers it. |
| **Email** | Reads it from a [connected inbox](#connect-an-inbox), or waits for you to [push it](#push-a-code). |
| **Text Message** | Waits for you to [push it](#push-a-code). |
| **Passkey** | Enterprise plan. |

### Authenticator app

<Tabs>
  <Tab title="In the app">
    1. Choose **Authenticator App**.
    2. Pick the app the site uses: **Google Authenticator**, **Microsoft Authenticator**, **Okta Verify** or **Oracle Mobile Authenticator**. Google Authenticator covers any standard authenticator setup code.
    3. Paste the key into **Authenticator Key**, or click **Scan QR** and upload a screenshot of the site's QR code.
    4. Click **Save**.

    <img src="https://mintcdn.com/skyvern/amcBKP0IBwdNswdN/images/cloud/totp-setup-2.png?fit=max&auto=format&n=amcBKP0IBwdNswdN&q=85&s=e45189b85a2f76bba81b4f794e8d4b4b" alt="Authenticator App selected with Google Authenticator, the Authenticator Key field and the Scan QR button" width="1338" height="780" data-path="images/cloud/totp-setup-2.png" />
  </Tab>

  <Tab title="With code">
    Add `totp` and `totp_type` to a password credential. `totp` takes the base32 key or the full `otpauth://totp/...` URI.

    ```python theme={null}
    credential = await client.create_credential(
        name="Vendor portal",
        credential_type="password",
        credential={
            "username": "you@example.com",
            "password": "your-password",
            "totp": "JBSWY3DPEHPK3PXP",
            "totp_type": "authenticator",
        },
    )
    ```

    Keys stored in Bitwarden (**Authenticator Key (TOTP)**), 1Password (**One-Time Password** field) or Azure Key Vault (`azure_vault_totp_secret_key`) work the same way.
  </Tab>
</Tabs>

**Finding the key.** When you turn on 2FA, most sites show a QR code with a "Can't scan?" or "Enter manually" link that reveals the key, a string like `JBSWY3DPEHPK3PXP`. Password managers that store 2FA keys show it on the login item. If 2FA is already on and you never saved the key, turn 2FA off and on again on the site to get a new one.

### Email and text message codes

1. Choose **Email** or **Text Message**.
2. Fill in the identifier: the address or phone number that receives codes. For **Email**, the **TOTP Identifier (Username or Email)** field lists your connected inboxes under **Select a connected account**. Click the code icon above it (**Enter manually**) to type an address instead. For **Text Message**, enter the number in **TOTP Identifier (Phone)**.
3. Click **Save**.

<img src="https://mintcdn.com/skyvern/amcBKP0IBwdNswdN/images/cloud/totp-setup-3.png?fit=max&auto=format&n=amcBKP0IBwdNswdN&q=85&s=faebcebafe5ab470f35111b425c639e4" alt="Email selected with the TOTP Identifier field showing Select a connected account" width="1338" height="766" data-path="images/cloud/totp-setup-3.png" />

When a run reaches the code prompt, Skyvern waits for a code sent to that identifier. In code, pass the same value as `totp_identifier` to `login`, `run_task` or `run_workflow`. In Studio, blocks that sign in have a **2FA Identifier** field.

### Connect an inbox

On the **2FA** or **Magic Links** tab, click **Connect an inbox**. It opens [Integrations](/docs/cloud/integrations), where you connect Gmail or Outlook. Skyvern then finds verification codes and magic links in that inbox on its own, with no forwarding.

### Push a code

Without a connected inbox, deliver email and SMS codes yourself.

<Tabs>
  <Tab title="In the app">
    While a run waits for a code, its page shows a "needs 2FA" banner with a countdown. Click **Enter Code** and paste the message.

    Or open the **2FA** tab on the Credentials page and use **Push a 2FA Code**:

    * **Identifier**: the email or phone that received the code.
    * **Verification content**: the whole message, or just the code. Skyvern extracts the code.
    * **OTP Type**: **Numeric code**, or **Magic link** for a one-time sign-in link.
    * **Add optional metadata**: when several runs wait on the same identifier, add the **Agent run ID**, **Agent ID** or **Task ID** to target one.

    Click **Send 2FA Code** (it reads **Send Magic Link** for magic links). The table below the form lists every code received, with filters for identifier, OTP type and count.

    <img src="https://mintcdn.com/skyvern/amcBKP0IBwdNswdN/images/cloud/credentials-2fa-push-form.png?fit=max&auto=format&n=amcBKP0IBwdNswdN&q=85&s=ff487b37f98ec2ca3e706a7a419d4805" alt="2FA tab with the Automatic 2FA from your inbox card and the Push a 2FA Code form" width="1620" height="898" data-path="images/cloud/credentials-2fa-push-form.png" />
  </Tab>

  <Tab title="With code">
    Forward the email or SMS to a script (a Gmail filter to a webhook, a Twilio function) that posts it to Skyvern. Send the whole message body; Skyvern extracts the code.

    <CodeGroup>
      ```python Python theme={null}
      await client.send_totp_code(
          totp_identifier="you@example.com",
          content="Your verification code is 847291",
          source="email_forwarder",
      )
      ```

      ```typescript TypeScript theme={null}
      await client.sendTotpCode({
        totp_identifier: "you@example.com",
        content: "Your verification code is 847291",
        source: "email_forwarder",
      });
      ```

      ```bash cURL theme={null}
      curl -X POST "https://api.skyvern.com/v1/credentials/totp" \
        -H "x-api-key: $SKYVERN_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "totp_identifier": "you@example.com",
          "content": "Your verification code is 847291",
          "source": "email_forwarder"
        }'
      ```
    </CodeGroup>

    * `source` is a free-text label for your own tracking.
    * Add `workflow_run_id`, `workflow_id` or `task_id` to target one run.
    * Set `type` to `magic_link` to push a sign-in link.
    * List received codes with `GET /v1/credentials/totp`. Filter with `totp_identifier`, `workflow_run_id` and `otp_type` (`totp` or `magic_link`); `limit` defaults to 50, maximum 200.
  </Tab>
</Tabs>

### Let Skyvern fetch codes from your endpoint

If codes land in a system you control, give Skyvern a URL to ask instead. Skyvern sends a `POST` with `task_id`, plus `workflow_run_id` and `workflow_permanent_id` for agent runs, signed with the `x-skyvern-signature` header (see [Going to production](/docs/developers/production) to verify it).

Answer with HTTP 200 and JSON:

```json theme={null}
{"verification_code": "123456"}
```

Until you have a code, answer 200 with `{"verification_code": ""}`. Any other status, or a body that isn't a JSON object, counts as an error. If the wait runs out, the run fails with `OTP_TIMEOUT` and its failure reason lists the status codes your endpoint returned.

Where to set the URL:

* **One-off login or task**: pass `totp_url`.
* **Agent**: set **2FA Verification URL** on the block that signs in. The run-level `totp_url` does not fill in a block whose field is empty. For a URL that changes per run, add an input such as `totp_url`, set the block field to `{{ totp_url }}` and pass the URL in `parameters`.

### Magic links

Some sites email a one-time sign-in link instead of a code. With a [connected inbox](#connect-an-inbox), Skyvern finds and opens the link. Otherwise paste the message into **Push a Magic Link** on the **Magic Links** tab, or push it with `send_totp_code`. Skyvern attaches the link to the waiting run and opens it.

### Passkey

**Passkey** appears as a 2FA option marked **Enterprise**. See [https://www.skyvern.com/pricing](https://www.skyvern.com/pricing) for plans. Passkey credentials can't use **Save browser session for future logins**.

### Timing

* Skyvern checks for pushed or fetched codes every 10 seconds, for up to 15 minutes.
* A pushed code stays usable for 10 minutes. The site's own code may expire sooner.
* Authenticator codes rotate every 30 seconds. When a code is split across several boxes (one digit per box), or the run uses the `skyvern-3.0` engine, Skyvern waits for a fresh code if fewer than 20 seconds remain, so the code doesn't expire mid-entry.

## Troubleshoot logins

| Symptom | Check |
| - | - |
| Run ends on the login page and nothing was typed | The Login block's **Credential** field or the credential input is set, and the credential still exists. For a vault source, the vault tile on Integrations is connected and the item IDs are right. |
| "Invalid username or password" | Sign in by hand with the same values. Some sites want the email, not a username. Re-enter the password to rule out a trailing space. |
| Run times out waiting for a code (`OTP_TIMEOUT`) | The identifier on the credential or run matches the one you push to, character for character. Check the code list on the **2FA** tab or `GET /v1/credentials/totp`. For an endpoint, read the failure reason for the status codes it returned. |
| "Code expired" or the code is rejected | Push codes as soon as they arrive. On self-hosted servers, keep the clock in sync (NTP). Retry the run. |
| **Save browser session for future logins** fails | The **Login Page URL** opens the sign-in form. Add **Login instructions (optional)** for unusual flows such as SSO. Inline testing is not available for every 2FA method. |
| Extra captchas or "new device" prompts | Turn on **Use a consistent IP address**, or reuse a signed-in [browser profile](/docs/browsers#browser-profiles). |

For problems that aren't specific to credentials, see [Troubleshooting](/docs/developers/troubleshooting).

## Next

* [Browsers: sessions and profiles](/docs/browsers): keep a signed-in browser and reuse it.
* [Build in Studio](/docs/cloud/studio): add a Login block and credential inputs.
* [Integrations](/docs/cloud/integrations): connect inboxes and vaults.


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