> ## Documentation Index
> Fetch the complete documentation index at: https://www.1password.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Browser extension API reference

> The API on the 1Password extension's service worker: wait for initialization, turn on Agentic Mode, create an access request, check its status, and fill a granted login.

export const StatusBadge = ({children}) => <span className="op-status-badge not-prose">{children}</span>;

<StatusBadge>Partner preview</StatusBadge>

The 1Password browser extension exposes a public API as `globalThis.api` on its background service worker at `chrome-extension://<extension-id>/background/background.js`. Call it over the Chrome DevTools Protocol (CDP). See [Call the extension over CDP](/agentic-autofill/partners/fill#call-the-extension-over-cdp).

Each capability has its own version namespace:

| Namespace | What it does |
| - | - |
| `api.initialization.v1` | Reports whether the extension has finished starting up |
| `api.agenticMode.v1` | Turns Agentic Mode on and off for a tab or the whole browser |
| `api.agenticAutofill.v1` | Creates and checks access requests, and fills granted logins |

`api.agenticAutofill.v1` has three independent operations:

| Operation | What it does |
| - | - |
| `createAccessRequest` | Creates an access request and returns the app link that opens it in 1Password |
| `getAccessRequestStatus` | Returns the request's current state and, once resolved, the granted logins |
| `fillCredential` | Fills and submits one granted login in a tab |

The extension and the SDK work together. You can create and check a request with the SDK, then pass the granted reference to the extension to fill.

## Install the extension

Load the development build of the 1Password extension that 1Password sends you, or install it with the extension installation mechanism your browser stack uses. 1Password will announce in your partner channel when Agentic Autofill is available in **Nightly**, **Beta**, or **Stable**, and when to switch.

| Channel | Extension ID |
| - | - |
| Development | `hjlinigoblmkhjejkmbegnoaljkphmgo` |
| [Nightly](https://chromewebstore.google.com/detail/1password-nightly-%E2%80%93-passw/gejiddohjgogedgjnonbofjigllpkmbf) | `gejiddohjgogedgjnonbofjigllpkmbf` |
| [Beta](https://chromewebstore.google.com/detail/1password-beta-%E2%80%93-password/khgocmkkpikpnmmkgmdnfckapcdkgfaf) | `khgocmkkpikpnmmkgmdnfckapcdkgfaf` |
| [Stable](https://chromewebstore.google.com/detail/1password-%E2%80%93-password-mana/aeblfdkhhhdcdjpifhhbdiojplfjncoa) | `aeblfdkhhhdcdjpifhhbdiojplfjncoa` |

## Wait for the extension to be ready

`api.initialization.v1` is available as soon as the service worker starts. Its `state` property is `"initializing"`, `"ready"`, or `"failed"`. Wait with `whenSettled()` instead of polling `state`:

```typescript theme={null}
const initializationState = await globalThis.api.initialization.v1.whenSettled();

if (initializationState !== "ready") {
  throw new Error("The 1Password extension failed to initialize");
}
```

`whenSettled()` resolves to `"ready"` or `"failed"`. A `"failed"` result is final for the current service worker lifetime.

After initialization, check that `api.agenticMode` and `api.agenticAutofill` are present. If either is missing, the installed extension build or its current configuration doesn't make this API available.

## Turn on Agentic Mode

Agentic Mode stops normal 1Password browser behavior, such as inline suggestions and save prompts, from exposing the person's state in pages your agent controls. Turn it on every time your agent starts working in the browser. `fillCredential` fails with `agenticModeNotEnabled` in a tab that Agentic Mode doesn't cover.

Turn it on for one tab when the agent starts working in that tab:

```typescript theme={null}
const response = await globalThis.api.agenticMode.v1.enable({ tabId });
```

Leave out the argument to turn it on for the whole browser, including tabs opened later:

```typescript theme={null}
const response = await globalThis.api.agenticMode.v1.enable();
```

Turn it off when the agent gives up control:

```typescript theme={null}
await globalThis.api.agenticMode.v1.disable({ tabId }); // one tab
await globalThis.api.agenticMode.v1.disable(); // the whole browser
```

Turning off the whole-browser scope releases every tab that agent controls. You can't turn off a single tab while whole-browser Agentic Mode is on.

`enable` and `disable` return this envelope:

```typescript theme={null}
type AgenticModeResponse =
  | { success: true }
  | {
      success: false;
      error: {
        code: "invalidRequest" | "internal" | "tab_not_found" | "tab_unavailable" | "tab_invalid";
      };
    };
```

Check `success` before you continue. Turning on a tab you already control returns `tab_invalid`. Turning on a tab that another agent controls returns `tab_unavailable`.

## Response envelope

Every Agentic Autofill operation takes the person's current access token and their integration key, and resolves to an envelope rather than throwing:

```typescript theme={null}
type Response<T> =
  | { success: true; result: T }
  | { success: false; error: { code: string } };
```

Check `success` before you read `result` or `error`.

## createAccessRequest

Creates an access request for one to five logins. The result contains the created request and an app link that presents it to the person in 1Password.

```typescript theme={null}
const createResponse = await globalThis.api.agenticAutofill.v1.createAccessRequest({
  accessToken,
  integrationKey,
  requests: {
    version: 2,
    goal: "Manage SaaS subscriptions",
    entries: [
      {
        type: "login",
        parameters: { website: "https://github.com" },
        keywords: ["personal"],
        reason: "Cancel the GitHub Pro subscription",
      },
    ],
  },
});
```

### Parameters

<ParamField body="accessToken" type="string" required>
  The current OAuth access token for the person's connection.
</ParamField>

<ParamField body="integrationKey" type="string" required>
  The integration key for the same connection.
</ParamField>

<ParamField body="requests" type="object" required>
  The request content.

  <Expandable title="properties">
    <ParamField body="version" type="number" required>
      The access request schema version. Always `2`.
    </ParamField>

    <ParamField body="goal" type="string">
      What the overall task is, in plain language. Up to 140 characters.
    </ParamField>

    <ParamField body="entries" type="object[]" required>
      One to five logins. Each is `{ type: "login", parameters: { website }, reason?, keywords? }`. `website` is the complete HTTP or HTTPS URL where the login will be used. `reason` is up to 100 characters. `keywords` is 1 to 5 strings of 1 to 50 characters. Don't include entry IDs. See [Create an access request](/agentic-autofill/partners/access-requests) for every rule.
    </ParamField>
  </Expandable>
</ParamField>

Describe what your agent needs, not a specific 1Password item. 1Password asks the person to pick the login that satisfies each entry.

### Result

```typescript theme={null}
{
  success: true,
  result: {
    accessRequest: {
      path: "accounts/<account>/credential-broker-access-requests/<request-id>",
      id: "<request-id>",
      identity: "<target-identity-path>",
      state: "pending",
      createdAt: "<RFC 3339 timestamp>",
      goal: "Manage SaaS subscriptions",
      entries: [
        {
          id: "<entry-id>",
          type: "login",
          parameters: { website: "https://github.com" },
          keywords: ["personal"],
          reason: "Cancel the GitHub Pro subscription",
        },
      ],
    },
    appLink: "onepassword://grant-brokered-access?access_request_reference=<opaque-reference>",
  },
}
```

<ResponseField name="accessRequest.id" type="string">
  The request ID. Pass it to `getAccessRequestStatus` as `accessRequestUUID`.
</ResponseField>

<ResponseField name="accessRequest.entries[].id" type="string">
  The ID 1Password assigned to each entry. Use it to match grants to entries.
</ResponseField>

<ResponseField name="appLink" type="string">
  Opens the request in 1Password. Open it on a device that has 1Password installed. Treat it as opaque: don't parse it, change it, or build it yourself, and don't log it or pass it through a model.
</ResponseField>

Creating or opening a request doesn't return the person's decision. Use `getAccessRequestStatus` to learn the outcome.

## getAccessRequestStatus

Returns the request's current state and, once it's resolved, the logins the person granted. The call returns right away: it doesn't wait for the person to decide. Your code owns polling, backoff, and timeouts.

```typescript theme={null}
const statusResponse = await globalThis.api.agenticAutofill.v1.getAccessRequestStatus({
  accessToken,
  integrationKey,
  accessRequestUUID: createResponse.result.accessRequest.id,
});
```

### Parameters

<ParamField body="accessToken" type="string" required>
  The current OAuth access token for the person's connection.
</ParamField>

<ParamField body="integrationKey" type="string" required>
  The integration key for the same connection.
</ParamField>

<ParamField body="accessRequestUUID" type="string" required>
  The `accessRequest.id` returned when the request was created.
</ParamField>

### Result

While the person hasn't decided:

```typescript theme={null}
{
  success: true,
  result: {
    path: "accounts/<account>/credential-broker-access-requests/<request-id>/status",
    state: "pending",
    resolved: [],
  },
}
```

After they approve:

```typescript theme={null}
{
  success: true,
  result: {
    path: "accounts/<account>/credential-broker-access-requests/<request-id>/status",
    state: "resolved",
    resolved: [
      {
        entryId: "<entry-id>",
        reference: {
          reference: "accounts/<account>/capability/login/configurations/<configuration-id>",
        },
      },
      {
        // A login the person added that you didn't ask for has no entryId.
        reference: {
          reference: "accounts/<account>/capability/login/configurations/<configuration-id>",
        },
      },
    ],
  },
}
```

<ResponseField name="state" type="string">
  `pending`, `resolved`, `denied`, or `failed`. `resolved`, `denied`, and `failed` are final: stop checking once you see one.
</ResponseField>

<ResponseField name="resolved" type="object[]">
  One item per granted login. Empty unless `state` is `resolved`.

  <Expandable title="properties">
    <ResponseField name="entryId" type="string">
      The entry this login satisfies. Absent, or `null`, when the person granted a login you didn't ask for.
    </ResponseField>

    <ResponseField name="reference" type="object">
      The credential reference, `{ reference: string }`. Pass the string inside it, `reference.reference`, to `fillCredential` as `resourcePath`.
    </ResponseField>
  </Expandable>
</ResponseField>

Match grants to your entries by `entryId`, never by position or type:

```typescript theme={null}
const requestedEntry = createResponse.result.accessRequest.entries[0];
const grant = statusResponse.result.resolved.find(
  ({ entryId }) => entryId === requestedEntry.id,
);
```

## fillCredential

Fills and submits one granted login in a browser tab. Call it on the page with the username and password fields, not an earlier page that only asks the person to choose a sign-in method. Agentic Mode must cover the tab.

```typescript theme={null}
const fillResponse = await globalThis.api.agenticAutofill.v1.fillCredential({
  accessToken,
  integrationKey,
  resourcePath: grant.reference.reference,
  tabId: 123,
});
```

### Parameters

<ParamField body="accessToken" type="string" required>
  The current OAuth access token for the person's connection. Refresh it before you fill: an expired token is reported as `fillFailed`.
</ParamField>

<ParamField body="integrationKey" type="string" required>
  The integration key for the same connection.
</ParamField>

<ParamField body="resourcePath" type="string" required>
  The credential reference from a resolved request: the string inside its reference object, `grant.reference.reference`. That's the same whether you checked the request with the extension or the SDK.
</ParamField>

<ParamField body="tabId" type="number" required>
  The Chrome tab ID of the tab to fill, from `chrome.tabs.query`. A CDP target ID doesn't work.
</ParamField>

### Result

```typescript theme={null}
{ success: true, result: { status: "fill_submitted" } }
```

<ResponseField name="status" type="string">
  `fill_submitted`: 1Password filled the login and submitted the form. It doesn't guarantee that the website accepted the login or finished signing in. Check the page before the agent continues.
</ResponseField>

On `fillFailed` and `autosubmitFailed`, 1Password clears what it filled.

## Error codes

| Code | Returned by | Meaning | What to do |
| - | - | - | - |
| `invalidRequest` | Agentic Autofill and Agentic Mode operations | The input is invalid. | Fix the request before you retry. |
| `tab_not_found` | `agenticMode.v1.enable` | The tab doesn't exist. | Pass the tab the agent is working in. |
| `tab_unavailable` | `agenticMode.v1.enable`, `disable` | The tab or whole-browser scope conflicts with another agent's control. | Don't take control from the other agent. Retry after it releases the tab or browser. |
| `tab_invalid` | `agenticMode.v1.enable`, `disable` | The change isn't valid, such as turning on a scope the same agent already controls, or turning off one tab while whole-browser mode is on. | Avoid duplicate `enable` calls. To release one tab from whole-browser mode, turn off whole-browser mode, then turn it on again for the tabs the agent still needs. |
| `invalidTabId` | `fillCredential` | The tab doesn't exist or can't be used. | Pass the tab the agent is working in. |
| `agenticModeNotEnabled` | `fillCredential` | Agentic Mode doesn't cover the tab. | Turn on Agentic Mode for the tab or the whole browser, then retry. |
| `noExistingCredentials` | Reserved | The granted login is no longer available. | Create a new access request and have the person approve it. |
| `fillFailed` | `fillCredential` | 1Password couldn't fill the login. An expired access token is also reported this way. | Refresh the token and retry when it's safe. If it keeps failing, report it to 1Password with the extension logs. |
| `autosubmitFailed` | `fillCredential` | 1Password couldn't submit the form and cleared the filled values. | Use your approved fallback, and report the website to 1Password. |
| `authenticationFailed` | `createAccessRequest`, `getAccessRequestStatus` | The OAuth access token is invalid or expired. | Refresh the access token, then retry. |
| `forbidden` | `createAccessRequest`, `getAccessRequestStatus` | This connection isn't allowed to access the resource. | Use the right person's connection. Don't retry unchanged. |
| `notFound` | `createAccessRequest`, `getAccessRequestStatus` | The resource doesn't exist. | Check the request ID and the connection. |
| `conflict` | `createAccessRequest`, `getAccessRequestStatus` | The operation conflicts with the resource's current state. | Get the current state before you decide whether to retry. |
| `rateLimitExceeded` | `createAccessRequest`, `getAccessRequestStatus` | You exceeded the rate limit. | Retry with backoff. |
| `timeout` | `createAccessRequest`, `getAccessRequestStatus` | The request to 1Password timed out. | Retry once when it's safe. |
| `networkError` | `createAccessRequest`, `getAccessRequestStatus` | The extension couldn't reach 1Password. | Restore connectivity, then retry. |
| `internal` | Agentic Autofill and Agentic Mode operations | An unexpected error, including a request that breaks a field limit. | Retry once when it's safe, then report it to 1Password with the extension logs. |


## Related topics

- [Set up your team to use 1Password developer tools](/get-started/secure-developers.md)
- [Use 1Password to securely provide credentials to AI agents](/agentic-autofill.md)
- [Use 1Password to securely authenticate the OpenAI CLI](/cli/shell-plugins/openai.md)
