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

# Claims

> The platform's only output: a signed statement that a policy held for the user's account.

A claim is the verifier's signed statement that a policy held for the user's account. It is the only thing a consumer reads. A consumer never re-derives facts from receipts.

## Fields

```rust theme={null}
pub struct Claim {
    pub outputs: Vec<TypedOutput>,          // the policy's declared outputs
    pub nullifier: Option<[u8; 32]>,
    pub context_digest: [u8; 32],           // binds the claim to one order
    pub receipt_digests: [[u8; 32]; 2],     // TEE_K then TEE_T
    pub profile_id: String,
    pub policy_id: String,
    pub circuit_id: CircuitId,
    pub config_hash: ConfigHash,
    pub verifier_epoch: u32,
    pub issued_at: u64,                     // unix seconds
    pub expires_at: u64,
    pub holder_binding: Option<HolderBinding>,
    pub signature: [u8; 64],                // Ed25519
}
```

Outputs come only from what the circuit proved: `Bool(true)` for a predicate that held, a bucket index, or a policy constant. A raw field value never becomes an output, so no payment detail leaves the phone. A Pesa payment claim carries one output, `satisfied`.

## The context digest

```
context_digest = H("zktls.context.v1" ‖ canonical(context))
```

For a Pesa order the context is `order_id`, `expected_amount`, `currency`, `payee_id_hash`, `payer_id_hash` and `reference`. The orchestrator rebuilds the same context from on-chain data and compares digests.

## The nullifier

```
nullifier = H("zktls.nullifier.v1" ‖ lp(scope) ‖ lp(field values...))
```

The circuit computes it, so the prover cannot choose it. It never includes a nonce, a blinder or an order ID. The same real-world payment always gives the same nullifier, even across a recapture, so it can be spent once. For a payment the scope is the rail and the field is the provider's transaction ID.

## Canonical encoding

Every hash input starts with a versioned ASCII domain, then each field in order. Variable-width fields have a 4-byte big-endian length prefix; fixed-width fields are raw. A decoder must refuse anything that does not re-encode to itself, so a claim has exactly one byte form. The receipt digests are ordered K then T and must never be sorted.

The transport form is the signed bytes followed by the 64-byte signature, base64-encoded as `claim.encoded` in the SDK.

## Verifying a claim

A consumer checks, in order:

1. **Signature.** Ed25519 over the canonical bytes (no prehash) by a claim key the trust bundle lists for `verifier_epoch`, on a verifier that is not revoked, in a valid epoch.
2. **Time.** `issued_at` at most 60 seconds ahead of the consumer's clock, and `expires_at` not passed.
3. **Holder binding.** Refuse a bound claim unless the consumer itself checked a holder proof.
4. **Policy.** `profile_id` and `policy_id` are the ones asked for, and the outputs say what is needed.
5. **Nullifier.** If a fact may be proved once, refuse a nullifier already seen.

The zkTLS SDK does checks 1 to 3 on the phone. Backend libraries in TypeScript, Go and Rust do them on a server: [Verify claims on your backend](/zktls-sdk/backend-verification). On Stellar, the [claims contract](/protocol/claims-contract) does them.


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