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

# Verify a credential

> Gate a feature in your app on a loyalty tier credential: request the tier, verify the presentation on your backend, and unlock or restrict access.

This recipe gates a feature on a loyalty tier. When a user opens a members-only page, your app asks for their tier credential, checks the result, and unlocks the page only for qualifying tiers.

It builds on the [verification quickstart](/get-started/quickstarts/verify-credentials), which covers the setup this recipe reuses: the server route that returns a Partner JWT and nonce, and the backend route that verifies the presentation.

## Prerequisites

* The verification quickstart's `/api/verification-token` and `/api/verify-presentation` routes.
* A schema with a `tier` claim, and a test user who holds a credential from an issuer you accept.

## Step 1: Create a program that requests the tier

1. In the [Developer Dashboard](https://developers.sandbox.air3.com/dashboard), go to **Verifier → Programs** and create a program.
2. Select the schema with the `tier` claim and the issuers you accept.
3. Request the `tier` claim, and publish the program.
4. Copy the verification program ID.

## Step 2: Request the tier when the user opens the page

<Tabs>
  <Tab title="Web">
    ```ts theme={null}
    async function verifyLoyaltyTier() {
      // Your server returns a Partner JWT and a one-time nonce
      const response = await fetch("/api/verification-token", { method: "POST" });
      const { token, nonce } = await response.json();

      const result = await airService.verifyCredential({
        authToken: token,
        programId: process.env.NEXT_PUBLIC_VERIFY_PROGRAM_ID,
        fieldsToDisclose: ["tier"],
        nonce,
      });

      if (result.status === "Compliant") {
        grantAccess(result.verifiablePresentation);
      } else {
        denyAccess(result.status);
      }
    }
    ```
  </Tab>

  <Tab title="Flutter">
    ```dart theme={null}
    final token = await fetchVerificationToken(); // your server endpoint

    final result = await airService.verifyCredential(
      authToken: token,
      programId: verifyProgramId,
    );

    if (result.status == "COMPLIANT") {
      grantAccess();
    } else {
      denyAccess(result.status);
    }
    ```
  </Tab>
</Tabs>

If the user doesn't hold the credential yet, pass an optional `redirectUrl` that points to the issuer's claim page.

## Step 3: Gate the feature on the result

Only `"Compliant"` results carry a `verifiablePresentation`. Other statuses, such as `"Non-Compliant"`, `"NotFound"`, `"Expired"`, or `"Revoked"`, have none. See the [response reference](/products/identity/verify#response) for the full list.

```ts theme={null}
async function grantAccess(presentation) {
  // Verify the SD-JWT on your backend before unlocking the feature.
  const check = await fetch("/api/verify-presentation", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ verifiablePresentation: presentation }),
  });
  if (check.ok) unlockFeature();
}

function denyAccess(status) {
  if (status === "Non-Compliant") {
    // Credential exists but fails the program's conditions — show a restricted view.
    return;
  }
  // Missing, pending, expired, or revoked credential — send the user to the
  // issuer flow or ask them to renew. Log `status` to distinguish the cases.
}
```

Check the disclosed `tier` value on your backend when it verifies the presentation, and unlock the page only for the tiers you allow. Wrap `verifyCredential` in `try/catch`: a cancelled consent dialog or a rejected token rejects the promise instead of returning a status.

<Warning>
  A `"Compliant"` result is checked in the user's browser, and the presentation wrapper is not signed. Before granting anything of value, verify the SD-JWT inside it on your backend, including the key-binding JWT against your nonce. See [Verify SD-JWT on your backend](/products/identity/verify-sd-jwt).
</Warning>

## Next steps

* Try it live: [member discount at a café](https://demo.air3.com/try-it-live/member-discount) or [show a seller you passed an ID check](https://demo.air3.com/try-it-live/meet-verified)

* [Verifying credentials](/products/identity/verify) for the full result shape and selective disclosure

* [Verifying your first credential](/get-started/quickstarts/verify-credentials) for the full setup

* [Schema Design](/products/identity/schema-overview) to understand which fields a program can check


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