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

# Telco & Subscriptions

> Issue subscriber credentials at activation. Roaming and MVNO partners verify KYC status with program-defined claims and user-controlled disclosure.

Carriers run identity verification for every new subscriber, but that verified status stays locked inside each operator's silo. A prepaid user who passes KYC at one carrier starts from zero at a roaming partner. AIR Kit lets you **issue a subscriber credential at activation** — so your KYC investment travels with the user across carriers, MVNOs, and partner services.

<Tabs>
  <Tab title="For business">
    ## The Problem

    Carriers spend on KYC at activation, then that verified status expires at the edge of their network. A prepaid subscriber who passed full identity verification at your MVNO is unknown to your roaming partner. Carrier partnerships that could unlock subscriber benefits require bilateral API agreements that take months to negotiate and are rarely worth the integration cost.

    ## What Changes With AIR Kit

    * **KYC travels with the subscriber** — Pass once at activation, accept everywhere in the ecosystem. Roaming partners verify the credential without calling your API or receiving subscriber data
    * **Loyalty tiers become cross-carrier benefits** — Issue a Silver or Gold subscriber credential. Partner MVNOs, device retailers, and service providers accept it to unlock benefits — no direct agreement required
    * **Age-restricted services gated properly** — Prove `isOver18` at point of service without transmitting or storing a subscriber's date of birth
    * **Churn intervention without data sharing** — Issue a "5-year subscriber" tenure credential. Retention partners can verify loyalty without accessing your customer database

    ## How It Works

    <Steps>
      <Step title="Subscriber activates and passes KYC">
        Your existing activation flow. Your existing identity verification. Nothing changes at this step.
      </Step>

      <Step title="AIR Kit issues a subscriber credential">
        At activation success, your backend fires one API call. A credential encoding KYC level, plan type, and age verification lands in the subscriber's AIR Account. Invisible to the subscriber.
      </Step>

      <Step title="Subscriber accesses a partner service">
        At a roaming partner, MVNO, or retail service, the subscriber authenticates with their AIR Account — the same login they use with you.
      </Step>

      <Step title="Partner verifies the requested claims">
        The partner calls one SDK method. A successful result includes a Verifiable Presentation with user-approved claims and any required proof. Configure the program to request KYC status without subscriber identifiers or documents when those are unnecessary.
      </Step>
    </Steps>

    ## What Crosses the Network — and What Doesn't

    This example requests limited claims with user approval. The Verification Program determines which claims are disclosed and whether a ZK proof is required.

    | What the Roaming Partner Receives | What Stays Private |
    | - | - |
    | COMPLIANT / NON\_COMPLIANT | MSISDN / phone number |
    | KYC level (basic / enhanced / full) | IMSI / SIM identifiers |
    | `isOver18: true` | Date of birth, full name |
    | Plan type (prepaid / postpaid) | Address, national ID |
    | Credential expiry | Billing history |

    ## What Compliance Will Ask

    **Does this satisfy roaming KYC obligations?**
    The credential attests to the level of KYC completed at your platform. Roaming partners accepting it are verifying that fact — the underlying compliance obligation remains with the issuing carrier.

    **Is subscriber PII transmitted to roaming partners?**
    A ZK proof can confirm KYC status without disclosing MSISDN or subscriber documents. Verification Programs may also request claims with user approval. Limit disclosures to what the roaming partner needs.

    **GDPR: where is subscriber data stored?**
    Raw source documents remain with the issuer. Encrypted credential payloads may be stored in DStorage, while users control decryption and disclosure. Subscriber claims and metadata can still be personal data; assess storage-provider locations and retention as part of your GDPR review.

    ## Integration at a Glance

    | | |
    | - | - |
    | **What your team builds** | One API call in your activation event handler |
    | **Engineering effort** | 1 backend engineer |
    | **Timeline to live** | 2–4 weeks |
    | **Changes to existing activation flow** | None |
    | **Roaming partner integration** | 3 lines of SDK code to verify |

    <CardGroup cols={2}>
      <Card title="Compliance FAQ" icon="shield-check" href="/solutions/compliance-faq">
        Full GDPR and regulatory answers.
      </Card>

      <Card title="Partner Economics" icon="coins" href="/solutions/partner-economics">
        Revenue model, fee structure, and reward.
      </Card>
    </CardGroup>
  </Tab>

  <Tab title="For developers">
    ## What You Can Build

    * **Subscriber credentials** — Issue a verified subscriber attestation the moment a user activates; roaming and partner carriers accept it without re-running identity checks
    * **Plan tier credentials** — Issue a "Premium" or "Business" tier badge that unlocks partner benefits automatically, with no bilateral API required
    * **Device ownership proofs** — Issue a verified device credential (IMEI-bound) for device insurance, warranty, and repair partner integrations
    * **Churn intervention** — Trigger a "Loyalty Reward" credential when a subscriber hits a retention milestone; partner services can verify and honour the benefit
    * **Roaming partner verification** — Let partner carriers verify a subscriber's KYC status via ZK proof without requesting subscriber documents
    * **Age-gated service access** — Gate adult content, gambling, or age-restricted add-ons behind an age credential without storing birthdates

    ## Architecture

    ```mermaid theme={null}
    graph TD
        A["User activates SIM / subscription"] --> B[Your Activation Backend]
        B -->|KYC pass + activation event| C[On-demand issuance]
        C --> D[Subscriber Credential in User's AIR Account]

        D --> E{User accesses partner service}
        E -->|Roaming carrier| F["Verify subscriber KYC\nUser-approved claims and proofs"]
        E -->|MVNO partner| G["Tier unlock\nno API integration required"]
        E -->|Device insurer| H["Device ownership proof"]
        E -->|Your retention offer| I["Loyalty reward gated by tenure credential"]
    ```

    ## Recommended Schemas

    ### Subscriber Credential

    ```json theme={null}
    {
      "title": "Subscriber Credential",
      "description": "Verified mobile subscriber attestation — no raw PII included",
      "properties": {
        "carrierId": {
          "type": "string",
          "description": "Issuing carrier identifier"
        },
        "planType": {
          "type": "string",
          "enum": ["prepaid", "postpaid", "business", "iot"],
          "description": "Subscription plan category"
        },
        "kycLevel": {
          "type": "string",
          "enum": ["basic", "enhanced", "full"],
          "description": "Level of identity verification completed at activation"
        },
        "activatedAt": {
          "type": "string",
          "format": "date-time"
        },
        "isOver18": {
          "type": "boolean"
        },
        "countryCode": {
          "type": "string",
          "description": "ISO 3166-1 alpha-2 — subscriber's registration country"
        }
      },
      "required": ["carrierId", "planType", "kycLevel", "activatedAt", "isOver18"]
    }
    ```

    ### Subscriber Loyalty Tier

    ```json theme={null}
    {
      "title": "Subscriber Loyalty Tier",
      "description": "Carrier loyalty tier based on tenure and spend",
      "properties": {
        "carrierId": { "type": "string" },
        "tier": {
          "type": "string",
          "enum": ["Standard", "Silver", "Gold", "Platinum"],
          "description": "Current loyalty tier"
        },
        "tenureMonths": {
          "type": "number",
          "description": "Months as active subscriber"
        },
        "memberSince": {
          "type": "string",
          "format": "date"
        }
      },
      "required": ["carrierId", "tier", "tenureMonths"]
    }
    ```

    <Warning>
      Never include MSISDN (phone number), IMSI, full name, or address in `credentialSubject`. Store only **attestations and derived facts** — `isOver18`, `kycLevel`, `planType`. ZK proofs let partner services confirm subscriber attributes without receiving any underlying data.
    </Warning>

    ## Implementation

    ### Step 1 — Issue subscriber credential at activation

    Your activation system already fires an event when a subscriber passes KYC and activates. Add one server-side issuance call to that event handler.

    ```javascript theme={null}
    // activation-handler.js
    const { getPartnerJwt } = require('./lib/jwt');
    // issuer-controlled helpers backed by your signing keys
    const { buildVc, signVc, encryptToHolder } = require('./lib/credential');

    const AIR_API_ORIGIN = 'https://air.api.sandbox.air3.com';
    const BASE_URL = 'https://api.sandbox.mocachain.org/v1';

    // Resolve the recipient, then build, sign, encrypt, and store the credential in DStorage
    async function issueEncryptedCredential({ recipientEmail, schemaId, credentialSubject }) {
      const token = await getPartnerJwt();

      const init = await fetch(`${AIR_API_ORIGIN}/v2/auth/initialize-user`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'x-partner-id': process.env.PARTNER_ID },
        body: JSON.stringify({ partnerJwt: await getPartnerJwt({ email: recipientEmail }) }),
      }).then((r) => r.json());

      const vc = buildVc({ holderDid: init.did, cnf: init.signingKey, schemaId, credentialSubject });
      const encrypted = encryptToHolder(signVc(vc), init.publicKey); // SD-JWT VC, encrypted to holder

      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,
          proofType: 'SD_JWT_VC',
          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(`Issuance failed: ${res.status}`);
      return res.json(); // { storagePath }
    }

    async function issueSubscriberCredential({ userEmail, activation }) {
      if (activation.kycStatus !== 'APPROVED') return;

      return issueEncryptedCredential({
        recipientEmail: userEmail,
        schemaId: process.env.SUBSCRIBER_CREDENTIAL_ID,
        credentialSubject: {
          carrierId: process.env.CARRIER_ID,
          planType: activation.planType,          // "prepaid" | "postpaid" | "business"
          kycLevel: activation.kycLevel,          // "basic" | "enhanced" | "full"
          activatedAt: new Date().toISOString(),
          isOver18: activation.age >= 18,
          countryCode: activation.countryCode,    // e.g. "AU", "SG"
        },
      });
    }

    // Hook into your existing activation pipeline
    activationPipeline.on('subscriber:activated', async ({ userEmail, activationData }) => {
      await issueSubscriberCredential({ userEmail, activation: activationData });
    });
    ```

    ### Step 2 — Issue loyalty tier on tenure milestone

    ```javascript theme={null}
    // churn-prevention.js — runs nightly or on billing cycle
    async function issueLoyaltyTierIfEligible({ userEmail, tenureMonths }) {
      const tier =
        tenureMonths >= 36 ? 'Platinum' :
        tenureMonths >= 24 ? 'Gold' :
        tenureMonths >= 12 ? 'Silver' : 'Standard';

      await issueEncryptedCredential({
        recipientEmail: userEmail,
        schemaId: process.env.LOYALTY_TIER_CREDENTIAL_ID,
        credentialSubject: {
          carrierId: process.env.CARRIER_ID,
          tier,
          tenureMonths,
          memberSince: getMemberSinceDate(userEmail),
        },
      });
    }

    // Run on each billing cycle to keep tiers current
    billingCycle.on('invoice:settled', async ({ userEmail, tenureMonths }) => {
      await issueLoyaltyTierIfEligible({ userEmail, tenureMonths });
    });
    ```

    ### Step 3 — Verify subscriber KYC at a roaming partner

    The roaming partner integrates AIR Kit as a verifier and selects a Verification Program. A successful SDK result includes a Verifiable Presentation with user-approved claims and any required proof. For this example, request KYC status without subscriber identifiers or documents.

    ```javascript theme={null}
    // roaming-partner.js (frontend — partner carrier's app)
    import { AirService } from '@mocanetwork/airkit';

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

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

    async function verifyRoamingSubscriber() {
      const result = await airService.verifyCredential({
        programId: process.env.ROAMING_VERIFY_PROGRAM_ID,
        // Verifier program rule: kycLevel === "enhanced" OR "full"
        // The verifier sees only: COMPLIANT / NON_COMPLIANT
        // No MSISDN, no name, no raw subscriber data
      });

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

    ### Step 4 — Gate age-restricted services

    ```javascript theme={null}
    // age-restricted-service.js (frontend)
    async function requireAgeVerification() {
      const result = await airService.verifyCredential({
        programId: process.env.AGE_18_VERIFY_PROGRAM_ID,
        // Program checks: isOver18 === true — subscriber's birthdate never exposed
      });
      return result.status === 'COMPLIANT';
    }

    const allowed = await requireAgeVerification();
    if (!allowed) showAgeVerificationPrompt();
    ```

    ## Key Patterns

    | Pattern | Reissue behavior | When to Use |
    | - | :-: | - |
    | Initial activation | Skip if exists | Issue once at first activation; don't re-issue if user reinstalls |
    | Plan upgrade / re-KYC | Revoke + reissue | Always reissue with updated `kycLevel` or `planType` |
    | Loyalty tier update | Revoke + reissue | Monthly billing cycle refresh |
    | Device credential | Skip if exists | One credential per device — immutable proof of ownership |

    ## Privacy Guarantee

    For this KYC-status check, configure the program to use a ZK proof without requesting MSISDN, IMSI, or subscriber documents. Successful verification returns a Verifiable Presentation with any approved claims and required proof; other programs may request additional disclosure.

    | What the Verifier Sees | What Stays Private |
    | - | - |
    | `COMPLIANT / NON_COMPLIANT` | MSISDN / phone number |
    | `kycLevel` (`basic` / `enhanced` / `full`) | IMSI / SIM data |
    | `isOver18: true` | Full name, date of birth |
    | `planType` | Address, national ID |
    | Credential expiry | Billing history |

    ## Examples

    The repo uses fintech and loyalty app names (KYC provider, lending platform, airline, hotel). The same code adapts to telco (carrier, roaming partner) via schema and branding — see each example's `schema.json` and the README's "Adapting to Your Vertical" section.

    <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="VIP Status Portability — Issuer" icon="github" href="https://github.com/MocaNetwork/air-examples/tree/main/vip-status-portability/issuer">
        Airline loyalty app: issues tier credential; user carries status to partner brands.
      </Card>

      <Card title="VIP Status Portability — Verifier" icon="github" href="https://github.com/MocaNetwork/air-examples/tree/main/vip-status-portability/verifier">
        Hotel chain app: verifies tier and grants equivalent perks (e.g. room upgrade, lounge).
      </Card>
    </CardGroup>

    ## Next Steps

    <Columns cols={2}>
      <Card title="On-demand Issuance — Concepts" icon="book" href="/products/identity/issuing-credentials#direct-issuance">
        On-demand issuance without user presence.
      </Card>

      <Card title="On-demand Issuance — API" icon="code" href="/api-reference/issuance-api">
        Full endpoint reference with error codes.
      </Card>

      <Card title="AIR for Fintech & Payments" icon="credit-card" href="/solutions/fintech">
        KYC portability patterns used in financial services.
      </Card>

      <Card title="Architecture & Data Flow" icon="diagram-project" href="/technicals/architecture">
        ZK proof flow end-to-end.
      </Card>
    </Columns>
  </Tab>
</Tabs>


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