# Claim a benefit

How a distributor claims a benefit for a holder through the distributor API's own calls, in the order they run: the challenge the wallet signs, the claim itself, the read and its link reissue, then the fulfilment list and its confirm. Every sample request and answer on this page is simulated: no real holder's data stands behind it.

## The order of the calls

A claim is not one call. The six calls below run in this order: ask for the message the wallet signs, record the claim with that signature, then read the claim and reissue its link when the holder needs a fresh one. The fulfilment calls come last, once the benefit waits on your own word.

Each call links its own page, which carries every field, every bound and every refusal. This page carries the order, the key scope each call wants and one sample of each request and answer.

POST [`/v1/claims/challenge`](https://docs.yutai.com/api/requestClaimChallenge): `claims.write` Callable today Ask for the challenge a wallet signs to claim a benefit

POST [`/v1/claims`](https://docs.yutai.com/api/recordClaim): `claims.write` Callable today Record a claim a wallet's own signature or a distributor's signed statement stands behind

GET [`/v1/claims/{id}`](https://docs.yutai.com/api/getClaim): `claims.read` Callable today Read one claim this distributor recorded

POST [`/v1/claims/{id}/link`](https://docs.yutai.com/api/reissueClaimLink): `claims.write` Callable today Reissue the redemption link of one claim this distributor recorded

GET [`/v1/fulfilments`](https://docs.yutai.com/api/listPendingFulfilments): `claims.read` Callable today List the fulfilments waiting on this distributor

POST [`/v1/fulfilments/{id}/confirm`](https://docs.yutai.com/api/confirmFulfilment): `claims.write` Callable today Confirm one fulfilment waiting on this distributor

## Ask for the message to sign

Send the benefit id and the wallets that will sign. The answer carries the exact message to sign and a one-time nonce. The message names the benefit and each wallet, and the nonce stands for one claim only, so a signature cannot be moved to another claim.

The wallet signs the message as it stands. Signing moves nothing and approves nothing.

POST [`/v1/claims/challenge`](https://docs.yutai.com/api/requestClaimChallenge) The key presents the `claims.write` scope. Callable today

The sample request for POST /v1/claims/challenge

```
{
  "benefitId": "bft_01K4V9QG000000000000000001",
  "wallets": [
    "0xabc0000000000000000000000000000000000001"
  ]
}
```

The sample answer for POST /v1/claims/challenge

```
{
  "message": "Yutai claim challenge\nTenant: tnt_01K4V9QG000000000000000001\nVenue: Sample venue (tnt_01K4V9QG000000000000000002)\nBenefit: Sample lounge access (bft_01K4V9QG000000000000000001)\nWallet: 0xabc0000000000000000000000000000000000001\nNonce digest: 0xa1b2c3d4a1b2c3d4a1b2c3d4a1b2c3d4a1b2c3d4a1b2c3d4a1b2c3d4a1b2c3d4\nExpires: 2026-10-11T12:05:00.000Z\nSigning moves nothing and approves nothing.",
  "nonce": "9f8e7d6c5b4a39281706f5e4d3c2b1a0",
  "wallets": [
    "0xabc0000000000000000000000000000000000001"
  ],
  "expiresAt": "2026-10-11T12:05:00.000Z"
}
```

## Make the claim

Sign the message with the wallet the challenge named, then send the benefit id, that wallet, the nonce and the signature. Yutai re-checks every rule at claim time, so an earlier eligibility answer never stands for one. A granted claim answers claimed with the claim's own id; a refused one answers refused with a reason.

The exchange example runs this part as working code: it asks for the challenge, has the example wallet sign, records the claim and reads the answer back. Follow it at [Example exchange](https://docs.yutai.com/examples/exchange); this page never copies its code.

POST [`/v1/claims`](https://docs.yutai.com/api/recordClaim) The key presents the `claims.write` scope. Callable today

The sample request for POST /v1/claims

```
{
  "benefitId": "bft_01K4V9QG000000000000000001",
  "wallet": "0xabc0000000000000000000000000000000000001",
  "nonce": "9f8e7d6c5b4a39281706f5e4d3c2b1a0",
  "signature": "0xababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab"
}
```

The sample answer for POST /v1/claims

```
{
  "status": "claimed",
  "claimId": "clm_01K4V9QG000000000000000001",
  "benefitId": "bft_01K4V9QG000000000000000001",
  "venueId": "tnt_01K4V9QG000000000000000002",
  "subjectKeys": [
    "evm:0xabc0000000000000000000000000000000000001"
  ],
  "alreadyClaimed": false,
  "createdAt": "2026-10-11T12:05:00.000Z",
  "code": "SAMPLE-LOUNGE-0001",
  "link": "https://app.yutai.com/r/clm_01K4V9QG000000000000000001?t=sample-redemption-token-0000000000000000000",
  "decision": {
    "eligible": true,
    "tree": [
      {
        "kind": "tenure",
        "met": true,
        "note": "The wallet's own trail stands 452 days behind this ask."
      }
    ]
  }
}
```

### Refusal reasons

Every reason the claim call can refuse with, in the contract's own order. Each reason is one word a caller branches on, and the sentence beside it is the one the SDK draws for that reason.

| Reason | What it answers |
| --- | --- |
| `unknown_challenge` | No claim challenge stands for the nonce this claim carried. Nothing was claimed. |
| `other_venue` | The challenge was issued through another distributor. Nothing was claimed. |
| `other_benefit` | The challenge was issued for another benefit. Nothing was claimed. |
| `not_consumed` | The challenge was never signed through. Nothing was claimed. |
| `no_holder` | No wallet of the challenge carries a standing link here. Nothing was claimed. |
| `conflicting_holders` | The challenge's wallets do not all name the same holder. Nothing was claimed. |
| `unknown_benefit` | The benefit this claim names is not one a holder can claim here. Nothing was claimed. |
| `no_offer` | No offer stands behind this benefit now. Nothing was claimed. |
| `already_claimed` | This benefit stands claimed already. Nothing was claimed. |
| `not_eligible` | The holder does not reach this benefit's rules now. Nothing was claimed. |

The decision rides a refusal only when the world itself refused, as with `not_eligible` and the reasons the benefit's own rulebook, holdings trail or country give. An `already_claimed` refusal carries none. The [errors and limits page](https://docs.yutai.com/errors) carries every refusal status the calls answer, each in its own words.

## Retry a claim

A claim is keyed by the challenge it carries, so one challenge can never mint a second claim. A retry of the same call answers the stored claim with `alreadyClaimed` true and the first grant's own `createdAt`, so the same call answers the same words twice.

A new challenge from the distributor that recorded the claim answers that claim again with `alreadyClaimed` true.

When another distributor holds the claim, the answer is the refusal `already_claimed`, with no decision beside it.

A granted answer carries the claim's own facts, and the holder's code or link when the benefit delivers one. Each field below carries the contract's own words:

`alreadyClaimed`: True when this call replayed a claim already recorded and false on the first grant; the same call answers the same words twice.

`createdAt`: The instant Yutai recorded the claim and its decision, as an ISO 8601 string.

`code`: The code the holder redeems, present only when the benefit delivers by code; a replay answers the same code, never a second take.

`link`: The holder's private redemption link, present on the first grant only; a replay carries no link.

A distributor that lost the link reissues one with the [link call](https://docs.yutai.com/api/reissueClaimLink).

## Read a claim and reissue its link

Read the claim by the id the claim answered, to see its state and its benefit in words. Reissue the link when the holder needs a fresh redemption link: only the last link issued opens the claim, so an earlier one stops working, and a rejected or taken back claim answers a refusal.

GET [`/v1/claims/{id}`](https://docs.yutai.com/api/getClaim) The key presents the `claims.read` scope. Callable today

The sample answer for GET /v1/claims/{id}

```
{
  "claimId": "clm_01K4V9QG000000000000000001",
  "benefitId": "bft_01K4V9QG000000000000000001",
  "benefitName": "Sample lounge access",
  "state": "granted",
  "createdAt": "2026-10-08T12:00:00.000Z"
}
```

POST [`/v1/claims/{id}/link`](https://docs.yutai.com/api/reissueClaimLink) The key presents the `claims.write` scope. Callable today

The sample answer for POST /v1/claims/{id}/link

```
{
  "claimId": "clm_01K4V9QG000000000000000001",
  "link": "https://app.yutai.com/r/clm_01K4V9QG000000000000000001?t=sample-redemption-token-0000000000000000000"
}
```

A reissue that fails inside Yutai answers in its own words: The redemption link was not reissued, so the previous link still stands. Nothing was changed.

## Confirm a fulfilment

List the fulfilments that wait on your own word, then confirm one with your own reference for the credit. The confirm marks the fulfilment as credited; a second confirm answers that it was already confirmed, so a retry never credits twice.

GET [`/v1/fulfilments`](https://docs.yutai.com/api/listPendingFulfilments) The key presents the `claims.read` scope. Callable today

The sample answer for GET /v1/fulfilments

```
{
  "fulfilments": [
    {
      "fulfilmentId": "ful_01K4V9QG000000000000000001",
      "claimId": "clm_01K4V9QG000000000000000001",
      "benefitId": "bft_01K4V9QG000000000000000001",
      "benefitName": "Sample lounge access",
      "state": "waiting on the venue",
      "createdAt": "2026-10-11T12:05:00.000Z"
    }
  ]
}
```

POST [`/v1/fulfilments/{id}/confirm`](https://docs.yutai.com/api/confirmFulfilment) The key presents the `claims.write` scope. Callable today

The sample request for POST /v1/fulfilments/{id}/confirm

```
{
  "venueReference": "Lounge visit 2026-10-08, table 12"
}
```

The sample answer for POST /v1/fulfilments/{id}/confirm

```
{
  "fulfilmentId": "ful_01K4V9QG000000000000000001",
  "claimId": "clm_01K4V9QG000000000000000001",
  "benefitId": "bft_01K4V9QG000000000000000001",
  "benefitName": "Sample lounge access",
  "state": "waiting on the venue",
  "createdAt": "2026-10-11T12:05:00.000Z"
}
```
