For AI agents: a documentation index is available at /llms.txt
Skip to main content

1Password

A 1Password service account lets a browser session fill secrets — usernames, passwords, API keys — straight from your vault without the value ever reaching your automation code. Browserless resolves the reference server-side, types it into the target field, and then locks the session down so the filled secret can't be read back or captured.

1Password is the first supported provider. You connect a 1Password service account once, then reference items by their op://Vault/Item/field path from inside a session.

This is useful when:

  • You need to log in with real credentials but don't want them in your code, environment variables, or logs.
  • You rotate secrets in 1Password and want every session to use the current value automatically.
  • You need an auditable record of every credential use, without the secret itself ever being recorded.
1Password vs Authenticated Profiles

Authenticated Profiles replay a previously captured logged-in state. 1Password fills live credentials to log in fresh each time. Use a profile to skip the login; use 1Password when you must actually authenticate with current secrets. The two can be combined.

Prerequisites
  • A Browserless cloud account
  • A 1Password service account with read access to the vault/items you want to use
  • A CDP client like Puppeteer or Playwright installed

How it works

A service account stores its 1Password token (encrypted at rest) and an optional domain allow-list, scoped to your API token. Inside a session you reference a secret by its op:// path and Browserless does the rest:

  1. Resolve — Browserless reads the referenced item from 1Password server-side using the stored service-account token.
  2. Fill — the resolved value is typed into the element you point at. The secret is never returned to your code, and never appears in CDP traffic, logs, or error messages.
  3. Lock down — once any secret has been filled, the session refuses to expose the page (see Security model), so the typed value can't be screenshotted or read back.
  4. Audit — the resolve is recorded against the service account: which op:// reference, on what origin, success or failure — but never the value.
Secrets stay server-side

Your automation only ever sends a reference (op://Vault/Item/field) and a target selector. The plaintext credential is resolved and typed inside Browserless; it never crosses back to the client.

Adding a service account

What these are called

In the dashboard and this guide these are 1Password service accounts (Profiles → 1Password Service Accounts). The REST API names the same object with integrationId and the /integrations/onepassword path.

The quickest way is the Profiles page in your Browserless dashboard, under 1Password Service Accounts:

  1. Create a 1Password service-account token

    In 1Password, create a service account and grant it read access to only the vault(s) holding the items you'll reference. Scope it as narrowly as possible — Browserless can resolve anything this service account can read. Copy the generated ops_… token.

  2. Add a service account

    Go to Profiles in the dashboard sidebar. Under 1Password Service Accounts, choose New Service Account. Pick the Provider (1Password), then paste the service-account token (ops_…) and a Label to recognise it later. The token is encrypted at rest and is never shown again.

  3. Restrict allowed domains (optional)

    By default the service account may fill on any site. To restrict it, turn on domain limiting and add the allowed origins (for example https://app.example.com; an entry also matches its subdomains). Either way, secrets are only ever typed into public https:// origins — http:// and private/internal addresses are always refused.

Filling a secret in a session

Connect with ?integrationId=<id>, navigate to the target page, then call the Browserless.loadSecret CDP method with the op:// reference and the field to fill.

CDP client required

Browserless.loadSecret is a CDP method, so use a CDP-capable client (Puppeteer, Playwright, or raw CDP).

import puppeteer from 'puppeteer-core';

const TOKEN = 'YOUR_API_TOKEN_HERE';
const INTEGRATION_ID = 'op_int_0a1b2c3d4e5f6a7b8c9d0e1f';

const browser = await puppeteer.connect({
browserWSEndpoint: `wss://production-sfo.browserless.io/chromium?token=${TOKEN}&integrationId=${INTEGRATION_ID}`,
});

const page = await browser.newPage();
await page.goto('https://app.example.com/login');

const cdp = await page.createCDPSession();

// Resolve op://Vault/Item/field from 1Password and type it into the field.
await cdp.send('Browserless.loadSecret', {
ref: 'op://Acme/login/username',
targetSelector: '#email',
});
await cdp.send('Browserless.loadSecret', {
ref: 'op://Acme/login/password',
targetSelector: '#password',
});

await page.click('button[type="submit"]');
await page.waitForNavigation();

await browser.close();

loadSecret returns { ok: true } on success, or { ok: false, error: '<code>' } on failure (it never throws). See Failure modes for each error code and how to handle it.

The credential is filled

The field now holds the secret value, but your code never saw it. Submit the form as usual.

Security model

Once a secret has been filled in a session, Browserless treats the page as sensitive and disables every channel that could read the value back:

ChannelBehaviour after a fill
Screenshots / PDFs / screencastsBlocked (CaptureBlocked)
Live URLs (and their live-view input-event stream)New connections refused; an already-streaming live view is torn down
Page-content reads (BQL evaluate, html, text, querySelector, cookies)Blocked (CaptureBlocked)
Screen recording (Browserless.startRecording)An in-progress recording is aborted and discarded on the first fill
Session replay (rrweb — DOM mutations)Keeps recording, but the filled values are scrubbed from the replay before it's stored

So the typed secret can't be screenshotted, captured on video, streamed to a live viewer, or read back — and any filled value is redacted from the rrweb session replay, logs, and CDP traces before they're persisted (raw and in common encodings).

Capture before you fill

If you need a screenshot, PDF, or page content, take it before the first Browserless.loadSecret call. After a fill these are rejected for the rest of the session.

Recommended: log in once, then reuse a profile

The lockdown is per session, so the cleanest way to get both secure login and unrestricted screenshots/PDFs is to combine 1Password with Authenticated Profiles: in one session fill your credentials, sign in, then capture the signed-in state with Browserless.saveProfile. Every later session starts already logged-in via ?profile=<name> — no secret is filled there, so captures and page reads work normally.

Don't want to script the login yourself? Autologin runs this entire one-time step for you — point it at a service account and it signs in to your allowed domains and saves the profile automatically.

// One-time: fill credentials from 1Password, sign in, save the auth state.
const cdp = await page.createCDPSession();
await cdp.send('Browserless.loadSecret', { ref: 'op://Acme/login/username', targetSelector: '#email' });
await cdp.send('Browserless.loadSecret', { ref: 'op://Acme/login/password', targetSelector: '#password' });
await page.click('button[type="submit"]');
await page.waitForNavigation();
await cdp.send('Browserless.saveProfile', { name: 'acme-prod' });

// Everywhere after: reuse the profile — no fill, no lockdown, screenshots work.
// wss://production-sfo.browserless.io?token=YOUR_API_TOKEN_HERE&profile=acme-prod

This keeps secrets out of all but the initial login, and the service account stays the single source of truth when credentials rotate — just re-run the one-time step to refresh the profile.

Other guarantees:

  • Origin safety — a secret is only ever typed into a public https:// origin; http:// and private/internal addresses are always refused. A service account can optionally set an allow-list to restrict fills to specific origins (and their subdomains); with no list — the default — any public https origin is allowed.
  • No leakage of the value — the plaintext is never returned to the client and is scrubbed from logs and error messages.
  • Encrypted at rest — the service-account token is encrypted and never returned by any API.
  • Audit log — every resolve is recorded with the op:// reference, target origin, and outcome — but never the secret value.

Failure modes

When loadSecret returns { ok: false, error }, error is one of these codes. Resolve/service-account errors:

CodeMeaningWhat to do
VaultUnreachableBrowserless couldn't reach 1Password (network/transport, or the service-account token couldn't authenticate).Retry; if it persists, check 1Password's status and that the service-account token is valid.
CredentialNotResolvedThe op:// reference didn't resolve — wrong vault/item/field, or the service account can't read it.Verify the reference and that the service account has access to that vault and item.
DomainNotAllowedThe page's origin isn't a permitted fill target — it isn't https://, it resolves to a private/internal address, or the service account has an allow-list the origin isn't on.Use a public https origin; if the service account sets an allow-list, add the origin to it.
CredentialIntegrationExpiredThe service-account token is expired or revoked.Rotate the token in 1Password and update the service account.

Fill-target errors (the credential resolved, but it couldn't be typed in):

CodeMeaningWhat to do
SelectorNotFoundThe selector (targetSelector over CDP) matched no element.Check the selector and that the field has rendered.
TargetNotFillableThe target isn't a fillable <input> / <textarea>.Point at a fillable element.
NoFocusedElementNo selector was given and nothing is focused.Pass a selector, or focus an input first.
AuditWriteFailedThe fill was refused because its audit record couldn't be written (fail-closed).Retry; if it persists, contact support.

Managing service accounts

Your 1Password service accounts are managed through the same REST surface, scoped to your API token.

List service accounts

curl "https://production-sfo.browserless.io/integrations/onepassword?token=YOUR_API_TOKEN_HERE"

Returns an array of service-account records (no token material). Paginate with limit and offset.

Get one service account

curl "https://production-sfo.browserless.io/integrations/onepassword/op_int_0a1b2c3d4e5f6a7b8c9d0e1f?token=YOUR_API_TOKEN_HERE"

View the audit log

curl "https://production-sfo.browserless.io/integrations/onepassword/op_int_0a1b2c3d4e5f6a7b8c9d0e1f/audit?token=YOUR_API_TOKEN_HERE"

Returns one entry per credential resolve — the op:// reference, target origin and selector, and the outcome. Secret values are never stored; the dashboard shows the same log for each service account.

Delete a service account

curl -X DELETE "https://production-sfo.browserless.io/integrations/onepassword/op_int_0a1b2c3d4e5f6a7b8c9d0e1f?token=YOUR_API_TOKEN_HERE"

Deletion is permanent; the encrypted service-account token is removed.

FAQ

Can my automation read the secret value?

No. You send a reference (op://Vault/Item/field) and a target selector; Browserless resolves and types the value inside the session. It is never returned to the client, and after the first fill the page-read and capture channels are disabled so it can't be extracted.

Why is my screenshot/evaluate returning CaptureBlocked?

Because a secret has already been filled in that session. Capture and page-content reads are disabled after the first Browserless.loadSecret to prevent the value from leaking. Run any captures before filling, or in a separate session.

Why did loadSecret return ok: false?

Common causes: the targetSelector didn't match an element on the page, the page isn't a permitted origin (not https://, a private address, or not on the service account's allow-list when one is set), or the op:// reference doesn't resolve (wrong vault/item/field, or the service account lacks access).

How is this different from Authenticated Profiles?

A profile replays a previously captured logged-in state (cookies/storage). 1Password logs in fresh by filling live credentials. Use a profile to skip login entirely; use 1Password when you must authenticate with current secrets — for example when the session needs to perform a real sign-in. They can be used together.

Further reading