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

# AIR for Fintech & Payments

> Issue KYC credentials, income proofs, and compliance attestations without storing PII. Enable privacy-preserving financial verification with AIR Kit.

Traditional KYC and compliance verification requires every platform to collect, store, and protect sensitive personal data — creating liability, compliance risk, and repeated friction for users. AIR Kit lets you **issue a KYC credential once**, and any partner platform can verify it via zero-knowledge proof without ever accessing raw PII.

## What You Can Build

* **KYC credentials** — Issue a verified identity attestation the moment a user passes KYC; partner platforms accept it without re-running the check
* **Income / accreditation proofs** — Issue an "Accredited Investor" credential backed by income verification, provable via ZK proof
* **Compliance gating** — Gate DeFi pools, financial products, or high-value transactions behind credential checks
* **Age verification** — Prove a user is 18+ or 21+ without revealing their birthdate to the verifier
* **Cross-border compliance** — One credential, accepted at every partner service in the ecosystem

## Architecture

```mermaid theme={null}
graph TD
    A["User completes KYC\nwith your KYC provider"] --> B["KYC Provider webhook / callback"]
    B --> C[Your Backend]
    C -->|Direct Issuance| D[AIR Kit / Moca Chain]
    D --> E[KYC Credential in User's AIR Account]

    E --> F{User accesses financial product}
    F -->|Your platform| G["Verify KYC credential\nNo PII stored or transferred"]
    F -->|Partner DeFi protocol| H["ZK proof: user is KYC'd"]
    F -->|Cross-border service| I[Compliance check passed]
```

## Recommended Schema

### KYC Attestation

```json theme={null}
{
  "title": "KYC Attestation",
  "description": "Identity verification attestation — no raw PII included",
  "properties": {
    "kycLevel": {
      "type": "string",
      "enum": ["basic", "enhanced", "institutional"],
      "description": "Level of KYC verification completed"
    },
    "kycProvider": {
      "type": "string",
      "description": "Name of the KYC provider (e.g. 'Jumio', 'Onfido')"
    },
    "verifiedAt": {
      "type": "string",
      "format": "date-time"
    },
    "countryCode": {
      "type": "string",
      "description": "ISO 3166-1 alpha-2 country code"
    },
    "isOver18": {
      "type": "boolean"
    },
    "isOver21": {
      "type": "boolean"
    }
  },
  "required": ["kycLevel", "kycProvider", "verifiedAt", "isOver18"]
}
```

<Warning>
  Never include raw PII — full name, passport number, date of birth, address — in `credentialSubject`. Store only **attestations and derived facts**. ZK proofs let verifiers confirm `isOver18 === true` without seeing any underlying data.
</Warning>

## Implementation

### Step 1 — Issue KYC credential from your KYC webhook

```javascript theme={null}
// kyc-webhook.js  — called when your KYC provider sends a completion event
const { getPartnerJwt } = require('./lib/jwt');
// issuer-controlled helpers backed by your signing keys
const { buildVc, signVc, encryptToHolder } = require('./lib/credential');

const BASE_URL = 'https://api.sandbox.mocachain.org/v1';

async function issueKycCredential({ userEmail, kycResult }) {
  if (kycResult.status !== 'APPROVED') return; // only issue on success

  const token = await getPartnerJwt();

  // Resolve the user's AIR Account
  const init = await fetch(`${BASE_URL}/auth/initialize-user`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'x-partner-auth': token },
    body: JSON.stringify({ email: userEmail }),
  }).then((r) => r.json());

  // Build, sign (BJJ_SIG_2021), and encrypt the credential to the holder
  const schemaId = process.env.KYC_CREDENTIAL_ID;
  const vc = buildVc({
    holderDid: init.did,
    schemaId,
    credentialSubject: {
      kycLevel: kycResult.level,             // "basic" | "enhanced" | "institutional"
      kycProvider: 'YourKYCProvider',
      verifiedAt: new Date().toISOString(),
      countryCode: kycResult.countryCode,    // e.g. "US", "GB"
      isOver18: kycResult.age >= 18,
      isOver21: kycResult.age >= 21,
    },
  });
  const encrypted = encryptToHolder(signVc(vc), init.publicKey);

  // Store the encrypted envelope in DStorage
  const res = await fetch(`${BASE_URL}/dstorage/vcs`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'x-partner-auth': token },
    body: JSON.stringify({
      holderDid: init.did,
      schemaId,
      expiresAt: vc.expirationDate,
      data: encrypted.encryptedData,
      iv: encrypted.iv,
      authTag: encrypted.authTag,
      encryptedKey: encrypted.dataEncPublicKey,
      externalId: vc.id,
    }),
  });
  if (!res.ok) throw new Error(`KYC credential issuance failed: ${res.status}`);
  return res.json(); // { storagePath }
}

// Express webhook endpoint
app.post('/webhooks/kyc', async (req, res) => {
  const { userEmail, kycResult } = req.body;
  await issueKycCredential({ userEmail, kycResult });
  res.json({ ok: true });
});
```

### Step 2 — Gate financial products with credential verification

```javascript theme={null}
// fintech-gate.js  (frontend)
import { AirService } from '@mocanetwork/airkit';

import { AirService, BUILD_ENV } from "@mocanetwork/airkit";

const airService = new AirService({ partnerId: process.env.PARTNER_ID });
await airService.init({ buildEnv: BUILD_ENV.SANDBOX });

async function requireKyc() {
  const result = await airService.verifyCredential({
    programId: process.env.KYC_VERIFY_PROGRAM_ID,
    // Verifier program rule: kycLevel is "enhanced" or "institutional"
  });

  if (result.status !== 'COMPLIANT') {
    throw new Error('KYC_REQUIRED');
  }
  return result;
}

// Gate a high-value transaction
try {
  await requireKyc();
  proceedWithTransaction();
} catch (err) {
  if (err.message === 'KYC_REQUIRED') showKycOnboarding();
}
```

### Step 3 — Age verification gate (no birthdate exposed)

```javascript theme={null}
// age-gate.js  (frontend)
async function requireAgeVerification() {
  const result = await airService.verifyCredential({
    programId: process.env.AGE_18_VERIFY_PROGRAM_ID,
    // Program checks: isOver18 === true  — verifier never sees the actual age/DOB
  });
  return result.status === 'COMPLIANT';
}
```

## Privacy Guarantee

The ZK proof flow means the verifier receives **only a boolean result**. No raw KYC data, no PII, no liability for the verifier to store sensitive documents.

| What the Verifier Sees           | What Stays Private              |
| -------------------------------- | ------------------------------- |
| `COMPLIANT / NON_COMPLIANT`      | Full name, passport / ID number |
| Credential expiry date           | Date of birth                   |
| KYC level (`basic` / `enhanced`) | Residential address             |
| Issuer DID                       | Income figures, net worth       |
| `isOver18: true`                 | Actual age                      |

## Examples

<CardGroup cols={2}>
  <Card title="KYC Passport — Issuer" icon="github" href="https://github.com/MocaNetwork/air-examples/tree/main/kyc-passport/issuer">
    KYC provider app: issues credentials once; user carries the proof to other platforms.
  </Card>

  <Card title="KYC Passport — Verifier" icon="github" href="https://github.com/MocaNetwork/air-examples/tree/main/kyc-passport/verifier">
    Lending platform app: verifies the credential and accepts the proof without re-running KYC.
  </Card>

  <Card title="ZK Age Verification — Issuer" icon="github" href="https://github.com/MocaNetwork/air-examples/tree/main/zk-age-verification/issuer">
    Identity provider app: issues age credential; user proves 18+ without revealing date of birth.
  </Card>

  <Card title="ZK Age Verification — Verifier" icon="github" href="https://github.com/MocaNetwork/air-examples/tree/main/zk-age-verification/verifier">
    iGaming platform app: verifies age-gate (e.g. 18+) via ZK proof; no PII received.
  </Card>
</CardGroup>

## Next Steps

<Columns cols={2}>
  <Card title="Direct Issuance — Concepts" icon="book" href="/airkit/usage/credential/direct-issuance">
    Server-side issuance without user presence.
  </Card>

  <Card title="Architecture & Data Flow" icon="diagram-project" href="/airkit/guides/overview">
    ZK proof flow end-to-end.
  </Card>

  <Card title="Partner Authentication" icon="key" href="/airkit/usage/partner-authentication">
    JWT signing setup for your backend.
  </Card>

  <Card title="Verifying Credentials" icon="shield-check" href="/airkit/usage/credential/verify">
    SDK verification reference.
  </Card>
</Columns>
