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

# Quickstart 3: Credential Verification

> Verify credentials with AIR Kit using zero-knowledge proofs — configure verification programs and gate features based on Compliant or Non-Compliant results.

<Warning>
  **BLOCKING — Partner JWKS endpoint required.** `issueCredential`, `verifyCredential`, and Issue on Behalf all fail until you (1) host a **public HTTPS JWKS URL**, (2) register it in **Dashboard → Account → General → JWKS URL**, and (3) sign your Partner JWT with a `kid` that matches a key in that JWKS. Localhost is not reachable from AIR servers — use an HTTPS tunnel (ngrok, cloudflared) or deploy. See [JWKS endpoint setup](/airkit/usage/jwks-setup).
</Warning>

If you are looking to **verify credentials on chain** this guide will help you achieve that.

This Quickstart will walk you through:

* Setting up verification programs
* Verifying user credentials with zero-knowledge proofs
* Handling verification results

## What You'll Build

By the end of this Quickstart, you will have:

* A **verification program** configured for your app
* Code that **verifies credentials** from users
* Application logic that reacts to verification results (e.g., grant/deny access)

## Before Starting, Ensure You Have

* Completed or understood the [Login & Sessions Quickstart](/airkit/quickstart/) (recommended but not mandatory)
* Completed the [Credential Issuance Quickstart](/airkit/quickstart/issue-credentials) (to have credentials to verify)
* Node.js v18+ installed
* A **public HTTPS JWKS URL registered in the Developer Dashboard** — `verifyCredential` calls fail without it. See [JWKS endpoint setup](/airkit/usage/jwks-setup).

## Step 1: Install the AIR Kit

```bash theme={null}
npm install @mocanetwork/airkit
```

## Step 2: Get Your Partner ID

1. Go to the <a href="https://developers.sandbox.air3.com" target="_blank" rel="noreferrer">Developer Dashboard</a> and login with your EOA wallet
2. Connect your wallet
   <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/wallet_connect.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=512a8ad299c5a541fe0b14be08ea1b2b" alt="Wallet connect" width="2938" height="1760" data-path="images/guides/wallet_connect.png" />
3. Navigate to the [**Accounts section**](https://developers.sandbox.air3.com/dashboard/general) on the navigation bar on the left. Then go to the **General section**. Copy the **Partner ID** and **Verifier DID** from the partner account information and settings
   <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/get_partner_id.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=55a5173ce860834ffe12aa5230446b76" alt="Get Partner ID connect" width="2336" height="1322" data-path="images/guides/get_partner_id.png" />

## Step 3: JWKS endpoint + Partner JWT (required — blocks issue/verify)

`verifyCredential` cannot succeed until your Partner JWT is signed with a key that AIR can validate against your registered JWKS URL. This is two pieces of work, in this order:

### Step 3a: Implement the JWKS endpoint

Host a public HTTPS endpoint that returns your Partner JWT public key as a JWKS document, then register the **full URL** in the Developer Dashboard at **Account → General Settings → JWKS URL**. If your issuer and verifier share a Partner ID, one JWKS registration (typically the issuer's) covers both — the verifier signs with a `kid` present in that same JWKS.

The canonical Next.js route used in [`air-examples`](https://github.com/MocaNetwork/air-examples) is `app/api/.well-known/jwks/route.ts`:

```ts app/api/.well-known/jwks/route.ts theme={null}
import { NextResponse } from "next/server";
import * as jose from "jose";

export async function GET() {
  const publicKeyPEM = `-----BEGIN PUBLIC KEY-----\n${process.env.PARTNER_PUBLIC_KEY!}\n-----END PUBLIC KEY-----`;
  const publicKey = await jose.importSPKI(publicKeyPEM, process.env.SIGNING_ALGORITHM!);
  const jwk = await jose.exportJWK(publicKey);

  return NextResponse.json({
    keys: [{ ...jwk, kid: process.env.NEXT_PUBLIC_PARTNER_ID!, use: "sig", alg: process.env.SIGNING_ALGORITHM! }],
  });
}
```

Localhost is not reachable from AIR servers — for local dev, use an HTTPS tunnel (ngrok, cloudflared) and register the tunnel URL. Full procedure on the [JWKS endpoint setup](/airkit/usage/jwks-setup) page.

### Step 3b: Sign the Partner JWT

With JWKS live and registered, sign your Partner JWT server-side using the matching private key, with a `kid` header that appears in your JWKS. Detailed signing examples for Node.js, Java, C#, and Go are in [Partner Authentication](/airkit/usage/partner-authentication).

## Step 4: Understanding Credential Verification

A **verifiable credential** is a secure, digital version of a credential record that can be verified using zero-knowledge proofs.

To start verifying a credential, you need to first set up a verification program and question set that validates the proof.

**Example:** Proving age > 18 without sharing full ID. Say you want to enter a bar and prove you are above 18 without showing your ID or sharing your birth date - you can leverage the age credential that was issued. You send the zero-knowledge proof (not your full passport) to the bar. The proof only answers the question: "Is this person over 18?" and nothing more.

## Step 5: Prerequisites

* Go to the [**Faucet**](https://faucet.mocachain.org), connect your wallet, and get \$MOCA test tokens
  <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/moca_faucet.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=a4bc65b6a893c7d9239aa5287378a153" alt="Moca Faucet" width="2336" height="863" data-path="images/guides/moca_faucet.png" />
* Go to Developer Dashboard -> Verifier -> Fee wallet and deposit funds
  <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/fund_fee_wallet.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=b28c6859eb3fccb4ae57955d8bab307a" alt="fund fee wallet" width="2336" height="1319" data-path="images/guides/fund_fee_wallet.png" />

## Step 6: Setup Verification Program

* Navigate to the [**Verifier tab**](https://developers.sandbox.air3.com/dashboard/verifier/programs) and click **"Create Program"**
  <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/setup_verifier_program.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=54dfd657c5b6b5a7f36666d8a722445c" alt="Moca verifier program" width="2336" height="1319" data-path="images/guides/setup_verifier_program.png" />
* Select the schema configured for credential issuance (e.g., `kyc_schema`)
* Configure your query with **question set**, operator, and attribute values
  <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/configure_query_set.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=b68940b0237d99eceefa9b6549d7b419" alt="Moca verifier program setup" width="2336" height="1322" data-path="images/guides/configure_query_set.png" />
* Add necessary details like **Issuer ID** and hit save. Your program will be created
  <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/create_verifier_program.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=da1a61590f99fb2cd92ca7ecdc4d9569" alt="Moca verifier program created" width="2336" height="1337" data-path="images/guides/create_verifier_program.png" />
* Now that you have the verifier program created, click on Details and Apply the program.
  <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/apply_program.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=3cf0f4d98fefab58216fcc658a391f6d" alt="Moca verifier program setup" width="2336" height="1321" data-path="images/guides/apply_program.png" />
* Once applied the status of the program will change from Draft to Active
  <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/deployed_program.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=1570d11d7198441bcb2d2479bbba7f59" alt="Moca verifier program deployed" width="2336" height="1261" data-path="images/guides/deployed_program.png" />

## Step 7: Verifying Credentials Using the Example App

You can verify credentials using the [Example app](https://developers.sandbox.air3.com/example/verify)

* Generate a JWT token on the demo app interface
  <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/verifier_jwt.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=c5fdf855034f296ade465068018cf35f" alt="verifier_jwt" width="2336" height="1328" data-path="images/guides/verifier_jwt.png" />

* Enter the verifier DID, verifier program ID and a custom redirect URL to issue credential (if required) and click on "Start Credential Verification"
  <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/start_verification.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=cf7f77435509310b98c925253eb21c9c" alt="start verification" width="2336" height="618" data-path="images/guides/start_verification.png" />

* The widget will guide you through the verification process.
  <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/verification_process_start.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=6aa0c6a793f025906dbc0d0ed6e97f5f" alt="Verification process start" width="2336" height="1315" data-path="images/guides/verification_process_start.png" />

* Once completed you will be able to verify the result on the UI as compliant or non compliant. In this case the result of verification will be compliant as the age was 20 and location was netherlands \[we were verifying adult users outside of the USA].
  <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/verification_complete.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=dc14a1ee31ee2424ad5b38c4a76e956a" alt="verification_complete" width="2252" height="498" data-path="images/guides/verification_complete.png" />

* You can verify the credential verification under the records section in the Verifier Tab.
  <img src="https://mintcdn.com/mocanetwork/A7O8YBP7ecQMKF6C/images/guides/verify_verification.png?fit=max&auto=format&n=A7O8YBP7ecQMKF6C&q=85&s=61069aa937279e5924edb6a3a3907c1d" alt="verify_verification" width="2336" height="1317" data-path="images/guides/verify_verification.png" />

## Step 8: Verifying Credentials Using the AIR Kit

After you've set up your verification program, you can verify credentials programmatically using the AIR Kit. This shows how to configure the verification flow, generate JWTs for authentication, and interact with the AIR SDK's verification API.

### Configure Verification Parameters

We need the following key parameters and configs:

```ts theme={null}
const [config, setConfig] = useState({
  verifierDid: env.VERIFIER_DID || "did:example:verifier123",
  programId: env.PROGRAM_ID || "c21hc030kb5iu0030224Qs",
  redirectUrlForIssuer: env.REDIRECT_URL_FOR_ISSUER || "http://localhost:8080/issue"
});
```

* `verifierDid`: The DID (Decentralized Identifier) of the verifier - identifies who is performing the verification.
* `programId`: The credential program being verified against. This must match the program ID on the Developer Dashboard.
* `redirectUrlForIssuer`: Used in cases when the credential doesnt exist and you want to redirect your user to claim a credential / issue one for them.

### Authentication (JWT Handling)

```ts theme={null}
const jwt = await generateJwt({ partnerId, privateKey });

// generateJwt is a utility function. Refer to the link below to look at its implementation
```

* `partnerId` and `privateKey` come from environment variables.
* JWT is used for authorization when calling airService.verifyCredential function.
  you can refer to [Authentication (Partner JWT)](/airkit/usage/partner-authentication) on how to generate JWT tokens
  > Tip: For production, generate JWTs on the backend to keep private keys secure.
* **Private Key**: Needed to sign JWTs, proving the verifier’s identity.
* **Generated JWT**: The signed token that authenticates API calls to AIR Service.

### Verify the Credential

Call `airService.verifyCredential` to verify the credential:

```ts theme={null}
const result = await airService.verifyCredential({
  authToken: jwt,
  programId: config.programId,
  redirectUrl: config.redirectUrlForIssuer
});

// Handle verification result
if (result.status === "Compliant") {
  // Verification successful - result includes zkProofs, transactionHash, and optionally cakPrivateKey
  console.log("Transaction hash:", result.transactionHash);
  console.log("Zero-knowledge proofs:", result.zkProofs);

  if (result.cakPrivateKey) {
    // Use cakPrivateKey to decrypt compliance data if needed
    console.log("Compliance decryption key available");
  }
} else {
  // Non-compliant status - result only contains status
  console.log("Verification status:", result.status);
}

setVerificationResult(result);
```

* Verifier must have a properly initialized `airService` and a logged in user.
* Call `airService.verifyCredential` with: - authToken: the JWT generated above. - programId: ties verification to the specific credential program configured on the Developer Dashboard. - redirectUrl: ensures redirect-based verification flows can complete.
  * Store Results: verificationResult is updated with the outcome (status + payload).
  * When `status` is `"Compliant"` and compliance encryption is enabled, `cakPrivateKey` is available for decrypting compliance data. See the [Verifying Credentials](/airkit/usage/credential/verify) documentation for details.

### Status Handling

Helper functions map the verification status into colors, icons, and descriptions:

```ts theme={null}
getStatusColor("Compliant"); // → green badge
getStatusIcon("Revoked"); // → 🚫
getStatusDescription("Expired"); // → "The credential has expired..."
```

<Tip>You can refer to this [Authentication (Partner JWT)](/airkit/usage/partner-authentication) for the implementation of these functions.</Tip>

## Summary of Verification Process

1. Set up verification program on the Developer Dashboard
2. Configure verifier DID, program ID, and redirect URL
3. Generate JWT for authentication
4. Call `airService.verifyCredential` to verify the credential
5. Handle verification results and display status

## Example Implementation

For a complete working example, check out the [AIR Credential Example repository](https://github.com/MocaNetwork/air-credential-example) which demonstrates:

* Full credential verification flow with React components
* Verification status handling and display
* Error handling and user feedback
* Complete integration with the AIR Kit's Credential Services

## Next Steps

Now that you know how to verify credentials, you can:

* **Issue credentials** - Learn how to use plug and play templates for issuing and verifying credentials in the [Plug and Play Quickstart](/airkit/templates/plug-and-play-issuer)
