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

# Vaults

> Let an agent sign in to the sites and services it works on, without passing secrets through the API.

export const Vaults = () => {
  const stroke = {
    fill: "none",
    stroke: "currentColor",
    strokeWidth: 1.75,
    strokeLinecap: "round",
    strokeLinejoin: "round"
  };
  const S = c => ({
    className: c,
    ...stroke
  });
  const icons = {
    agent: c => <svg viewBox="0 0 24 24" {...S(c)}><path d="M12 8V4H8" /><rect width="16" height="12" x="4" y="8" rx="2" /><path d="M2 14h2M20 14h2M15 13v2M9 13v2" /></svg>,
    lock: c => <svg viewBox="0 0 24 24" {...S(c)}><rect width="18" height="11" x="3" y="11" rx="2" /><path d="M7 11V7a5 5 0 0 1 10 0v4" /></svg>,
    vault: c => <svg viewBox="0 0 24 24" {...S(c)}><path d="m7.5 4.27 9 5.15" /><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z" /><path d="m3.3 7 8.7 5 8.7-5M12 22V12" /></svg>,
    globe: c => <svg viewBox="0 0 24 24" {...S(c)}><circle cx="12" cy="12" r="10" /><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" /><path d="M2 12h20" /></svg>
  };
  const Card = ({icon, title, sub, children, className = ""}) => <div className={className}>
      <div className="flex h-full flex-col rounded-xl border border-zinc-200 bg-white p-5 dark:border-zinc-800 dark:bg-zinc-950">
        <div className="flex items-center gap-2.5">
          <span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-200">{icon("h-5 w-5")}</span>
          <div>
            <div className="whitespace-nowrap text-base font-semibold leading-6 text-zinc-900 dark:text-zinc-100">{title}</div>
            {sub && <div className="whitespace-nowrap text-sm text-zinc-500 dark:text-zinc-400">{sub}</div>}
          </div>
        </div>
        {children}
      </div>
    </div>;
  const Arrow = ({top, bottom, dir = "right"}) => <div className="flex flex-col items-stretch justify-center gap-1 px-3 text-center text-xs leading-4 text-zinc-500 dark:text-zinc-400">
      {top && <span className="whitespace-nowrap">{top}</span>}
      <div className={`${dir === "left" ? "flex flex-row-reverse items-center text-zinc-400 dark:text-zinc-600" : "flex items-center text-zinc-400 dark:text-zinc-600"}`}>
        <span className="h-px flex-1 bg-current" />
        <svg className="-ml-px h-3 w-2 shrink-0" viewBox="0 0 8 12" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={dir === "left" ? {
    transform: "scaleX(-1)",
    marginLeft: 0,
    marginRight: -1
  } : undefined}>
          <path d="M1 1.5 6 6l-5 4.5" />
        </svg>
      </div>
      {bottom && <span className="whitespace-nowrap">{bottom}</span>}
    </div>;
  const Down = ({label}) => <div className="flex items-center gap-3 py-1 pl-5">
      <div className="flex flex-col items-center text-zinc-400 dark:text-zinc-600">
        <span className="h-6 w-px bg-current" />
        <svg className="-mt-px h-2 w-3" viewBox="0 0 12 8" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M1.5 1 6 6l4.5-5" /></svg>
      </div>
      <span className="text-xs text-zinc-500 dark:text-zinc-400">{label}</span>
    </div>;
  const Chip = ({children}) => <span className="whitespace-nowrap rounded-md bg-zinc-100 px-2 py-0.5 font-mono text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300">{children}</span>;
  const Group = ({label, children, className = ""}) => <div className={className}>
      <div className="flex h-full flex-col rounded-2xl border border-dashed border-zinc-300 p-4 dark:border-zinc-700">
        <div className="mb-4 text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400">{label}</div>
        {children}
      </div>
    </div>;
  return <div className="not-prose my-8 overflow-x-auto">
      <div className="flex min-w-[720px] items-stretch">
        <Group label="Agent side" className="flex shrink-0 flex-col">
          <Card icon={icons.agent} title="Agent" sub="never sees the value" className="my-auto">
            <div className="mt-4 flex flex-col items-start gap-2 text-sm text-zinc-600 dark:text-zinc-400">
              <Chip>fill_secret_at(field)</Chip>
              <div>Knows the field, not the secret</div>
            </div>
          </Card>
        </Group>

        <div className="flex min-w-[120px] flex-1 flex-col justify-center gap-6">
          <Arrow top="fill_secret_at" />
          <Arrow bottom="done" dir="left" />
        </div>

        <Group label="Secret side" className="shrink-0">
          <div className="flex items-stretch">
            <Card icon={icons.lock} title="Secret broker" sub="resolves by page URL" />
            <div className="flex min-w-[100px] flex-1 flex-col justify-center gap-6">
              <Arrow top="match URL" />
              <Arrow bottom="secret" dir="left" />
            </div>
            <Card icon={icons.vault} title="Your vault" sub="1Password, org-scoped" />
          </div>

          <Down label="enter_secret: types, then submits" />

          <div className="flex items-center gap-4 rounded-xl border border-zinc-200 bg-white p-4 dark:border-zinc-800 dark:bg-zinc-950">
            <span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-200">{icons.globe("h-5 w-5")}</span>
            <div className="whitespace-nowrap text-base font-semibold text-zinc-900 dark:text-zinc-100">Login form</div>
            <div className="flex flex-1 flex-col gap-1.5">
              <div className="h-6 rounded-md bg-zinc-100 dark:bg-zinc-800" />
              <div className="flex h-6 items-center rounded-md bg-zinc-100 px-2 font-mono text-xs tracking-[0.2em] text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400">••••••••</div>
            </div>
            <div className="rounded-md bg-zinc-900 px-3 py-1.5 text-xs font-medium text-white dark:bg-zinc-100 dark:text-zinc-900">Sign in</div>
          </div>
        </Group>
      </div>
    </div>;
};

<Vaults />

When an agent needs to sign in to a site or service, you don't pass the credentials through the API. Instead you register a vault (a link between your organization and an external secrets provider), and the agent pulls the right secret at the moment it needs it. Secrets never travel through your API requests and are never returned by any endpoint.

Today the only provider is [1Password](https://developer.1password.com/).

## What a vault stores

A vault config records two things:

* `op_vault_id`: which 1Password vault to read credentials from.
* A [service account token](https://www.1password.dev/service-accounts) authorizing access to that vault.

Manage vault configs through the [Vaults](/agents-api/vaults/create) endpoints: create, list, retrieve, update, rotate the token, delete, and health-check.

## Set up a vault

Creating a vault is not enough on its own: nothing uses it until you bind it to a [Browser](/agents-api/browser/configuration) environment.

<Steps titleSize="h3">
  <Step id="create-vault" title="Create a vault">
    Register the 1Password vault and a [service account token](https://www.1password.dev/service-accounts) that grants access to it. The token is validated against the provider, kept write-only, and never returned. See [Create a vault](/agents-api/vaults/create) for the full field list.

    <CodeGroup>
      ```bash cURL theme={"system"}
      curl -X POST https://agp.eu.hcompany.ai/api/v2/vaults \
        -H "Authorization: Bearer $HAI_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "name": "prod-1password",
          "provider_config": {"provider": "onepassword", "op_vault_id": "abcd1234efgh5678"},
          "token": "ops_eyJ..."
        }'
      ```

      ```python Python theme={"system"}
      from hai_agents import Client, OnePasswordConfig

      client = Client()

      vault = client.vaults.create_vault(
          name="prod-1password",
          provider_config=OnePasswordConfig(op_vault_id="abcd1234efgh5678"),
          token="ops_eyJ...",
      )
      print(vault.id)
      ```

      ```typescript TypeScript theme={"system"}
      import { HaiAgentsClient } from "hai-agents";

      const client = new HaiAgentsClient();

      const vault = await client.vaults.createVault({
        name: "prod-1password",
        providerConfig: { opVaultId: "abcd1234efgh5678" },
        token: "ops_eyJ...",
      });
      console.log(vault.id);
      ```
    </CodeGroup>
  </Step>

  <Step id="bind-vault" title="Bind it to a browser">
    Set the browser's `vault_id` to the vault's `id`, inline in an agent's `environments` list or on a catalog [environment](/agents-api/environments/create). The vault must belong to your organization.

    Vaults are only supported on cloud-hosted browsers (`host: "cloud"`, the default). Secrets are resolved and typed inside H's infrastructure and never leave it, so a browser running on your own device cannot bind a vault. Setting `vault_id` with `host: "user_device"` is rejected.

    <CodeGroup>
      ```bash cURL theme={"system"}
      curl -X POST https://agp.eu.hcompany.ai/api/v2/environments \
        -H "Authorization: Bearer $HAI_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "id": "signed-in-browser",
          "kind": "web",
          "vault_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
        }'
      ```

      ```python Python theme={"system"}
      client.environments.create_environment(
          id="signed-in-browser",
          kind="web",
          vault_id="f47ac10b-58cc-4372-a567-0e02b2c3d479",
      )
      ```

      ```typescript TypeScript theme={"system"}
      await client.environments.createEnvironment({
        id: "signed-in-browser",
        kind: "web",
        vaultId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      });
      ```
    </CodeGroup>
  </Step>

  <Step id="run-vault" title="Run with secret access">
    Run a session against an agent using that browser as usual. Whenever a credential in the vault matches the page the agent is on, the session offers it a [`fill_secret_at`](/agents-api/browser/configuration#actions) action to sign in. Leave `vault_id` unset to run without secret access.
  </Step>
</Steps>

## How secrets are matched

Agents never name a 1Password item directly. When an agent fills a credential, the item is selected automatically from the page's URL:

* Domain gating: an item is eligible only if the page's hostname equals, or is a subdomain of, one of the item's stored sites. An agent on `test.hcompany.ai` can use an item stored for `hcompany.ai` (a parent domain), but not one stored for `prod.hcompany.ai` (a sibling).
* Closest host wins: when several eligible items hold the same field, the one whose hostname matches the page most specifically is chosen. An exact host beats a parent-domain match, and a deeper subdomain beats a shallower one.
* Path breaks ties: if two items match the hostname equally well, the one whose stored path best prefixes the page URL wins. Path is only a tiebreaker, never a requirement.

Store one item per site so matching stays unambiguous.

## Endpoints

| Method   | Path                               | Description                                         |
| -------- | ---------------------------------- | --------------------------------------------------- |
| `POST`   | `/api/v2/vaults`                   | [Create a vault](/agents-api/vaults/create)         |
| `GET`    | `/api/v2/vaults`                   | [List vaults](/agents-api/vaults/list)              |
| `GET`    | `/api/v2/vaults/{vault_id}`        | [Retrieve a vault](/agents-api/vaults/retrieve)     |
| `PATCH`  | `/api/v2/vaults/{vault_id}`        | [Update a vault](/agents-api/vaults/update)         |
| `PUT`    | `/api/v2/vaults/{vault_id}/token`  | [Rotate the token](/agents-api/vaults/rotate-token) |
| `DELETE` | `/api/v2/vaults/{vault_id}`        | [Delete a vault](/agents-api/vaults/delete)         |
| `GET`    | `/api/v2/vaults/{vault_id}/health` | [Check health](/agents-api/vaults/health)           |

## Next steps

<CardGroup cols={2}>
  <Card title="Two-factor authentication" icon="shield-halved" href="/agents-api/two-factor-auth">
    Hand the agent one-time codes when a login asks for them.
  </Card>

  <Card title="Browser profiles" icon="user" href="/agents-api/browser/profiles">
    Start already signed in from saved cookies instead of typing credentials.
  </Card>
</CardGroup>
