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

Integrations

Credential integrations let a browser session fill secrets — usernames, passwords, API keys — straight from your secret manager 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.
Integrations vs Authenticated Profiles

Authenticated Profiles replay a previously captured logged-in state. Integrations log in fresh each time by filling live credentials. Use a profile to skip the login; use an integration 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

An integration stores a 1Password service-account token (encrypted at rest) and an allow-list of domains, 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 integration: 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.

Creating an integration

The quickest way is the Integrations page in your Browserless dashboard:

  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 — the integration can resolve anything the service account can read. Copy the generated ops_… token.

  2. Open Integrations

    Go to Integrations in the dashboard sidebar and choose New Integration.

  3. Add your service account

    Paste the service-account token and a label to recognise it later. The token is encrypted at rest and is never shown again.

  4. Set allowed domains

    List the origins the integration is permitted to fill on (for example https://app.example.com). A fill is only ever performed when the page's origin is on this list.

Filling a secret in a session

Connect with ?integrationId=<id>, navigate to a page on an allowed domain, 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)
Session recording & replay (rrweb — captures DOM and input events)Stopped on the first fill

So the typed secret is never recorded by replay, streamed to a live viewer, or captured in an input-event log.

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 integrations 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.

// One-time: fill credentials with the integration, 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 integration stays the single source of truth when credentials rotate — just re-run the one-time step to refresh the profile.

Other guarantees:

  • Domain allow-list — a fill is only performed when the page's origin is in the integration's allowedDomains.
  • 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/integration 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 in the integration's allowedDomains.Add the origin to the integration's allowed domains.
CredentialIntegrationExpiredThe service-account token is expired or revoked.Rotate the token in 1Password and update the integration.

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 integrations

Integrations are managed through the same REST surface, scoped to your API token.

List integrations

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

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

Get one integration

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's Activity view renders the same log.

Delete an integration

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's origin isn't in the integration's allowedDomains, or the op:// reference doesn't resolve for the service account (wrong vault/item/field, or the account lacks access).

How is this different from Authenticated Profiles?

A profile replays a previously captured logged-in state (cookies/storage). An integration logs in fresh by filling live credentials from 1Password. Use a profile to skip login entirely; use an integration 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