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

# Vault an item

> Let an item's owner send it to the brand's vault from the browser: check which vault takes it, have the owner approve, and cancel until the item is vaulted, with @endstate-sdk/web.

An owner can send an item to the brand's vault. The vault keeps the physical
item and confirms it has it with its own tap; the owner keeps ownership. Your
page offers the vault the brand chose, the owner approves it with their
Endstate wallet, and the request waits until the vault confirms.

Each step needs something different:

* **Offering the vault** (`useCanVault()`) needs only a recent **tap** of the
  item, not a wallet, so show the prompt as soon as the tap verifies, without
  waiting for the wallet.
* **Asking to vault** (`useVault()`) needs the tap, which is how the owner shows
  they hold the item, and the owner's **wallet**, which signs the approval.
* **Cancelling** (`useCancelVault()`) needs the wallet that approved the
  request. A tap is optional: with one, the call also waits until
  `useCanVault()` no longer names the request.

Vaulting is
enabled per environment; until it is, these calls answer `not_found.resource`.

## The flow

1. **Offer the vault.** `useCanVault()` reads the brand's default vault for the
   tapped item and whether it can go there now. Show the prompt only when
   `vaultable` is true.
2. **Ask to vault.** `vault({ vaultId })` has the wallet sign the owner's
   approval for that vault and opens the vault request. It reads `pending`
   until the vault confirms it has the item, and `useCanVault()` names it as
   `requestId` by the time `vault()` resolves.
3. **Cancel, if the owner changes their mind.** Until the item is vaulted,
   `cancelVault({ requestId })` withdraws the approval from the wallet. It
   resolves once Endstate sees the cancel, and by then `useCanVault()` no
   longer names the request, so the prompt offers the vault again.

```tsx React theme={null}
"use client";
import { useCancelVault, useCanVault, useVault } from "@endstate-sdk/web/react";

// Inside an <EndstateProvider> with a wallet configured.
function VaultPrompt() {
  const { data: eligibility } = useCanVault();
  const { vault, status } = useVault();
  const { cancelVault, status: cancelStatus } = useCancelVault();

  // canVault names the open request after vault(), and after a reload.
  const requestId = eligibility?.requestId;
  if (requestId) {
    return (
      <button
        disabled={cancelStatus === "pending"}
        onClick={() => cancelVault({ requestId })}
      >
        Cancel the vault request
      </button>
    );
  }
  if (!eligibility?.vaultable || !eligibility.vault) return null;
  const { id, name } = eligibility.vault;
  return (
    <button
      disabled={status === "pending"}
      onClick={() => vault({ vaultId: id })}
    >
      Vault this item with {name}?
    </button>
  );
}
```

```ts Vanilla theme={null}
// `session` came from a captured, verified tap; `wallet` from createWallet().
const { vaultable, vault, requestId } =
  await session.vaultRequests.eligibility();
if (vaultable && vault) {
  const request = await wallet.vault({ session, vaultId: vault.id });
  request.status; // "pending"

  // Later, until the item is vaulted:
  await wallet.cancelVault(request.id);
} else if (requestId) {
  // An earlier visit opened a request the owner can still cancel.
  await wallet.cancelVault(requestId);
}
```

## What the owner approves

`vault()` takes the vault id `canVault` returned, never one you pick. The wallet
asks Endstate for the approval to sign, and Endstate builds it from the vault
the brand allows, so a page cannot send an item to a vault the brand did not
choose. `cancelVault()` takes the `id` of the request `vault()` returns. After
a reload, `canVault` returns the same id as `requestId` until the item is
vaulted, so the owner can still cancel.

An item with an open vault request answers `vaultable: false`, so the prompt
does not come back on the next tap until the request is cancelled, rejected or
expires.

## Outcomes and the wallet

Each action hook returns `{ status, data, error, reset }`, with `status`
`"idle" | "pending" | "success" | "error"`. An outcome belongs to the tap, the
wallet and the sign-in it ran for: a new tap, another wallet, a sign-out or a
sign-in returns the hook to `"idle"`, and a call still in flight for the old one
never lands.

`useCanVault()` stays current through the owner's own actions, so a page
reads one answer. `vault()` reads eligibility again before it resolves, and
`cancelVault()` reads it until Endstate no longer names the request, which
can take a few seconds after the network includes the cancel. Without a tap
session there is nothing to read, so `cancelVault()` resolves once the network
included the cancel. If Endstate has not seen the cancel within two minutes,
or the read fails, the call still resolves and `useCanVault()` keeps its last
answer; call `canVault()` again later.

The cancel is sent from the owner's wallet with the cost covered by Endstate;
the owner never pays for it. It resolves with
`{ transactionHash, address, resourceId }`.

## Errors

**vault.not\_permitted** `403`

The brand no longer allows that vault. Read `canVault` again and offer the
vault it names.

**vault.in\_progress** `409`

The item already has an open vault request, or is vaulted. While the owner can
still cancel the request, `canVault` names it as `requestId`.

**submission.not\_executable** `409`

`cancelVault()` found the request past the point where it can be cancelled: it
was cancelled, the vault rejected it, it expired, or the item is vaulted.
Nothing was sent.

**not\_found.resource** `404`

Vaulting is not enabled for this environment.

See [Errors](https://docs.endstate.io/conventions/errors.md#vault-requests) for every vault request code.

## See also

> [card] [useVault](https://docs.endstate.io/sdks/web/reference/react/useVault.md)
>
> The hook that signs the approval and opens the request.

> [card] [session.vaultRequests](https://docs.endstate.io/sdks/core/reference/session-vaultrequests/eligibility.md)
>
> The same calls in `@endstate-sdk/core`, without a wallet.


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