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

# Claim ownership

> Hand a tapped item to its first owner from the browser in a single call - React's useClaim() hook or Vanilla session.claims.create - settlement, idempotency, and errors, with @endstate-sdk/web.

Claiming is the payoff of a tap. A customer taps your product, you confirm it is genuine, and you hand them the item. This is the **first assignment of ownership out of a tap**: the item has no prior owner, so no one has to authorize the hand-off. You supply only the recipient's wallet address, and the session token the tap opened authorizes the write.

That first-ownership shape is what sets a claim apart from a [transfer](/sdks/web/transfer), which moves an item between existing owners. Because a claim has no prior owner to sign, Endstate signs and submits it for you - so claiming is a single call.

In React, that call is the `useClaim()` hook. The rest of this page leads with the hook and shows the Vanilla primitive alongside it.

## The flow

In React, wrap the claim page in `EndstateProvider` and drive the claim from `useClaim`. With `autoVerify` on (the default) the provider captures and verifies the landing tap on mount, so `useSession().item` is the item the customer tapped and `useWallet()` provisions their address in the background. `useClaim().claim()` with **no arguments** claims into that wallet address, and because `wait` defaults to `true` it auto-settles before it resolves. `status` is a `ClaimStatus` you render the UI from.

The Vanilla path picks up from a session you captured and verified yourself - the [browser quickstart](/sdks/web/quickstart) golden path and [Tap capture](/sdks/web/tap-capture) cover how you get one, and [Tap sessions](/sdks/core/tap-sessions) covers what it is - then calls `session.claims.create({ to })` and settles with `waitUntilSettled`.

<CodeGroup>
  ```tsx React theme={null}
  "use client";
  import {
    EndstateProvider,
    useSession,
    useWallet,
    useClaim,
  } from "@endstate-sdk/web/react";

  // Wrap the claim page once. autoVerify (on by default) captures and verifies the
  // landing tap on mount; the wallet provisions from your identity credential.
  function App() {
    return (
      <EndstateProvider
        publishableKey="end_pk_..."
        wallet={{
          // A fresh, single-use identity token minted by your own backend.
          getIdentityToken: () => mintIdentityToken(),
          // Your auth state - getIdentityToken runs only once this is true.
          enabled: isSignedIn,
        }}
      >
        <ClaimButton />
      </EndstateProvider>
    );
  }

  function ClaimButton() {
    const { item } = useSession(); // the verified item from the landing tap
    const { status: wallet } = useWallet(); // "ready" once the address exists
    const { claim, status, error } = useClaim();

    // claim() with no args claims into the wallet address; wait defaults true, so it
    // resolves once settled. status is a ClaimStatus that drives the UI.
    const busy = status === "claiming" || status === "settling";
    return (
      <button disabled={wallet !== "ready" || busy} onClick={() => claim()}>
        {status === "claimed" ? "Claimed" : `Claim ${item?.name ?? "item"}`}
      </button>
    );
  }
  ```

  ```ts Vanilla theme={null}
  import { createWallet } from "@endstate-sdk/web";

  // Provisioned at page load - see the Wallet page for the lifecycle.
  const wallet = createWallet({
    publishableKey: "end_pk_...",
    // Single-use identity credential, issued fresh every call - never cache it.
    getIdentityToken: () => mintIdentityToken(),
  });

  // `session` came from a captured, verified tap. The wallet gives a customer who
  // brings no address of their own a recipient address to claim into.
  const { address } = await wallet.ready();

  // The unit is bound to the session scope, so you pass only the recipient.
  const claim = await session.claims.create({ to: address });
  claim.status; // "claiming"

  // Endstate submits it for you; poll to a terminal status.
  const settled = await session.claims.waitUntilSettled(claim.id);
  settled.status; // "claimed" | "expired" | "failed"
  ```
</CodeGroup>

The claim takes a single field:

<ParamField body="to" type="string">
  The recipient's EVM wallet address (`0x` followed by 40 hex characters). In
  React, omit it and `claim()` claims into the provisioned wallet address from
  `useWallet()`; pass one to target a specific address. Vanilla's
  `session.claims.create` requires it - for a customer who brings no address of
  their own, this is `(await wallet.ready()).address`.
</ParamField>

<Note>
  A claim reads `unit_id` from the session's scope, so you never pass it. A
  session freshly opened by `verify()` (including the provider's `autoVerify`)
  already carries that scope; one you [adopted from a bare
  token](/sdks/core/tap-sessions#adopting-a-session-you-already-hold) does not,
  and the call throws `EndstateSessionScopeError` until you `await
      session.refreshScope()`.
</Note>

Endstate signs and submits the claim for you (`execution` defaults to `endstate_relay`), so a single `claim()` is the whole write; to broadcast it yourself from your own funded account or relayer instead, pass `execution: "client_broadcast"` and send the returned payload - see [`session.claims.create`](/sdks/core/reference/session-claims/create).

## Settlement

A claim starts `idle`, and once `claim()` runs it moves through `claiming` (signed and submitted) and `settling` (awaiting confirmation) to one of three terminal statuses:

* **`claimed`** - done. Ownership has transferred and settled.
* **`expired`** - the signed authorization's window (about 30 minutes) passed
  before the claim settled. Create a new claim.
* **`failed`** - the submission did not settle. Create a new claim.

A thrown failure lands `status` on `error` and puts the `Error` on `useClaim().error`. In React you drive the whole progression off `status`; because `wait` defaults to `true`, `claim()` resolves only once the status is terminal, so you never write a poll loop.

In Vanilla, `session.claims.waitUntilSettled` does that waiting for you:

```ts theme={null}
const settled = await session.claims.waitUntilSettled(claim.id);
settled.status; // "claimed" | "expired" | "failed"
```

It reads the claim on a backoff - starting at 1s, doubling to a 5s ceiling, and
giving up at 120s by default - all overridable through its options. To poll
yourself instead, call `session.claims.get(claimId)`, or
`endstate.claims.get(unitId, claimId)` from a secret-key client on your backend.
Pass `wait: false` to opt out of auto-settlement and drive it yourself. On
`expired` or `failed`, create a new claim.

## Idempotency

Creating a claim is a mutation, so it is safe to retry with an idempotency key.
Pass `idempotencyKey`; omit it and one is generated for you. Replaying the same
key with the same body returns the original claim rather than opening a second
one; the same key with a different body is rejected as a conflict.

```tsx theme={null}
// React: pass it to claim(). Vanilla: session.claims.create({ to }, { idempotencyKey }).
await claim({ idempotencyKey: "claim-order-12345" });
```

## Errors

Every failure carries a branchable `code`. In React it surfaces on
`useClaim().error` with `status` set to `error`; in Vanilla
`session.claims.create` throws it. For how those errors behave and what retries
automatically, see [Errors and retries](/sdks/core/errors-and-retries); for the
full claim code list - `unit.not_minted`, `claim.in_progress`,
`claim.already_to_recipient`, `session_token.wrong_chip`, and the rest - see
[`session.claims.create`](/sdks/core/reference/session-claims/create) and the
[Claim a unit](/guides/claim-a-unit) guide.

## Next steps

<CardGroup cols={2}>
  <Card title="session.claims.create" icon="arrow-right-left" href="/sdks/core/reference/session-claims/create">
    The claim request body, execution modes, and every error code.
  </Card>

  <Card title="Transfer ownership" icon="repeat" href="/sdks/web/transfer">
    Move an item between existing owners - the sibling action to a claim.
  </Card>

  <Card title="Wallet" icon="wallet" href="/sdks/web/wallet">
    Provision the recipient address the claim sends the item to.
  </Card>

  <Card title="Claim a unit" icon="list-check" href="/guides/claim-a-unit">
    The credential-neutral walkthrough and the full error matrix.
  </Card>
</CardGroup>
