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

# User Verification & Credential Validation

> Implement user eligibility verification using Galxe's credential system for access control

## Overview

Galxe's B2B Credential API provides **read-only** access for:

* User eligibility verification
* Credential information retrieval
* Access control validation
* Requirement checking

<Info>
  **Important**: The B2B API is designed for verification and querying only. Credential management is handled through the [Galxe Dashboard](https://galxe.com).
</Info>

## Understanding Credentials

Credentials are verification proofs that users earn through various activities:

### Popular Credential Types

* **`EVM_ADDRESS`**: Ethereum-compatible wallet verification
* **`SOLANA_ADDRESS`**: Solana wallet verification
* **`TWITTER`**: Twitter-based verification
* **`DISCORD`**: Discord membership verification
* **`GITHUB`**: GitHub contribution verification
* **`EMAIL`**: Email verification

### Common Verification Methods

* **Social Media**: `TWITTER_FOLLOW`, `DISCORD_MEMBER`, `GITHUB_CONTRIBUTOR`
* **Blockchain**: `CONTRACT_NFT_HOLDER`, `WALLET_BALANCE`, `SNAPSHOT_ORG`
* **Interactive**: `QUIZ`, `SURVEY`, `VISIT_LINK`
* **Custom**: `API`, `REST`, `GRAPHQL`

## Basic Credential Queries

### Get Credential Information

```graphql theme={null}
query GetCredentialInfo($credId: ID!) {
  credential(id: $credId) {
    id
    name
    description
    credType
    credSource
    referenceLink
    itemCount
    lastUpdate
    syncStatus
    curatorSpace {
      id
      name
    }
    chain
  }
}
```

**Response**:

```json theme={null}
{
  "data": {
    "credential": {
      "id": "312969464",
      "name": "Twitter Follower Verification",
      "description": "Users who follow @galxe on Twitter",
      "credType": "TWITTER",
      "credSource": "TWITTER_FOLLOW",
      "referenceLink": "https://twitter.com/galxe",
      "itemCount": 150000,
      "lastUpdate": 1699200000,
      "syncStatus": "SYNCED",
      "curatorSpace": {
        "id": "40",
        "name": "BNB Chain"
      },
      "chain": "BSC"
    }
  }
}
```

### Check User Eligibility

```graphql theme={null}
query CheckUserEligibility($credId: ID!, $address: String!) {
  credential(id: $credId) {
    id
    name
    eligible(address: $address)
    credType
    credSource
    syncStatus
  }
}
```

**Response**:

```json theme={null}
{
  "data": {
    "credential": {
      "id": "312969464",
      "name": "Twitter Follower Verification",
      "eligible": 1,
      "credType": "TWITTER",
      "credSource": "TWITTER_FOLLOW",
      "syncStatus": "SYNCED"
    }
  }
}
```

**Eligibility Values**:

* `1`: User is eligible
* `0`: User is not eligible

### Quest-Specific Eligibility

```graphql theme={null}
query CheckQuestEligibility($credId: ID!, $address: String!, $campaignId: ID!) {
  credential(id: $credId) {
    eligible(address: $address, campaignId: $campaignId)
    name
    syncStatus
  }
}
```

## Basic Implementation

### Get Credential Information

```javascript theme={null}
async function getCredentialInfo(credId, accessToken) {
  const response = await fetch('https://graphigo-business.prd.galaxy.eco/query', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'access-token': accessToken
    },
    body: JSON.stringify({
      query: `query GetCredentialInfo($credId: ID!) {
        credential(id: $credId) {
          id
          name
          description
          credType
          credSource
          referenceLink
          itemCount
          lastUpdate
          syncStatus
          curatorSpace {
            id
            name
          }
          chain
        }
      }`,
      variables: { credId }
    })
  });
  
  const data = await response.json();
  return data.data.credential;
}
```

### Verify User Eligibility

```javascript theme={null}
async function verifyUserEligibility(credId, userAddress, accessToken, campaignId = null) {
  const response = await fetch('https://graphigo-business.prd.galaxy.eco/query', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'access-token': accessToken
    },
    body: JSON.stringify({
      query: `query CheckUserEligibility($credId: ID!, $address: String!, $campaignId: ID) {
        credential(id: $credId) {
          id
          name
          eligible(address: $address, campaignId: $campaignId)
          credType
          credSource
          syncStatus
          lastSync
        }
      }`,
      variables: { credId, address: userAddress, campaignId }
    })
  });
  
  const data = await response.json();
  const credential = data.data.credential;
  
  return {
    credentialId: credential.id,
    credentialName: credential.name,
    isEligible: credential.eligible === 1,
    credentialType: credential.credType,
    credentialSource: credential.credSource,
    isActive: credential.syncStatus === 'SYNCED',
    lastSync: credential.lastSync,
    userAddress,
    campaignId
  };
}
```

### Verify Multiple Credentials

```javascript theme={null}
async function verifyMultipleCredentials(credentialIds, userAddress, accessToken, campaignId = null) {
  const verificationPromises = credentialIds.map(credId => 
    verifyUserEligibility(credId, userAddress, accessToken, campaignId)
      .catch(error => ({
        credentialId: credId,
        error: error.message,
        isEligible: false,
        userAddress,
        campaignId
      }))
  );
  
  const results = await Promise.all(verificationPromises);
  
  const passed = results.filter(result => result.isEligible && !result.error);
  const failed = results.filter(result => !result.isEligible || result.error);
  
  return {
    userAddress,
    campaignId,
    totalChecked: credentialIds.length,
    passed: passed.length,
    failed: failed.length,
    allPassed: passed.length === credentialIds.length,
    results: { passed, failed }
  };
}
```

## Access Control System

### Simple Access Control

```javascript theme={null}
async function checkAccess(userAddress, requiredCredentials, accessToken, campaignId = null) {
  const verification = await verifyMultipleCredentials(
    requiredCredentials.map(req => req.credentialId),
    userAddress,
    accessToken,
    campaignId
  );
  
  // Check if all required credentials are met
  const requiredIds = requiredCredentials
    .filter(req => req.required !== false)
    .map(req => req.credentialId);
  
  const passedRequired = verification.results.passed.filter(result =>
    requiredIds.includes(result.credentialId)
  );
  
  const hasAccess = passedRequired.length === requiredIds.length;
  
  return {
    hasAccess,
    userAddress,
    campaignId,
    requirements: {
      total: requiredCredentials.length,
      required: requiredIds.length,
      satisfied: passedRequired.length
    },
    details: verification,
    missing: requiredCredentials.filter(req =>
      req.required !== false && 
      !passedRequired.some(passed => passed.credentialId === req.credentialId)
    )
  };
}
```

## Usage Examples

### Basic Verification

```javascript theme={null}
const accessToken = process.env.GALXE_ACCESS_TOKEN;

// Check single user eligibility
const result = await verifyUserEligibility(
  '312969464', // Twitter follower credential
  '0x1234567890123456789012345678901234567890',
  accessToken
);

console.log(`User eligible: ${result.isEligible}`);
console.log(`Credential: ${result.credentialName}`);
console.log(`Type: ${result.credentialType}`);
```

### Multi-Credential Verification

```javascript theme={null}
// Check multiple requirements
const credentialIds = ['312969464', '312969465', '312969466'];
const userAddress = '0x1234567890123456789012345678901234567890';

const multiResult = await verifyMultipleCredentials(credentialIds, userAddress, accessToken);

console.log(`Passed: ${multiResult.passed}/${multiResult.totalChecked} credentials`);
console.log(`Access granted: ${multiResult.allPassed}`);

if (!multiResult.allPassed) {
  console.log('Failed credentials:', multiResult.results.failed.map(f => f.credentialName || f.credentialId));
}
```

### Quest-Specific Verification

```javascript theme={null}
// Check eligibility for specific quest
const questResult = await verifyUserEligibility(
  '312969464',
  userAddress,
  accessToken,
  'GChdWUjXX3' // Quest ID
);

console.log(`Quest eligible: ${questResult.isEligible}`);
```

### Access Control System

```javascript theme={null}
// Define access requirements
const requirements = [
  { credentialId: '312969464', description: 'Twitter Follower', required: true },
  { credentialId: '312969465', description: 'Discord Member', required: true },
  { credentialId: '312969466', description: 'NFT Holder', required: false }
];

// Check user access
const accessCheck = await checkAccess(userAddress, requirements, accessToken);

if (accessCheck.hasAccess) {
  console.log('Access granted!');
  console.log(`Satisfied ${accessCheck.requirements.satisfied}/${accessCheck.requirements.required} required credentials`);
} else {
  console.log('Access denied. Missing requirements:');
  accessCheck.missing.forEach(req => {
    console.log(`- ${req.description}`);
  });
}
```

## Common Patterns

### User Onboarding Flow

```javascript theme={null}
async function onboardUser(userAddress, accessToken) {
  const requiredCredentials = [
    { credentialId: '312969464', description: 'Twitter Follower', required: true },
    { credentialId: '312969465', description: 'Discord Member', required: true }
  ];
  
  const verification = await verifyMultipleCredentials(
    requiredCredentials.map(r => r.credentialId), 
    userAddress,
    accessToken
  );
  
  if (verification.allPassed) {
    return { 
      status: 'approved', 
      message: 'Welcome! All requirements met.' 
    };
  } else {
    const missing = requiredCredentials.filter(req =>
      !verification.results.passed.some(p => p.credentialId === req.credentialId)
    );
    
    return { 
      status: 'pending', 
      message: `Please complete: ${missing.map(m => m.description).join(', ')}`,
      missingCredentials: missing
    };
  }
}
```

### Access Gate Implementation

```javascript theme={null}
async function checkGateAccess(userAddress, requiredCredId, accessToken, questId = null) {
  const result = await verifyUserEligibility(requiredCredId, userAddress, accessToken, questId);
  
  return {
    allowed: result.isEligible,
    reason: result.isEligible ? 'Access granted' : `Missing: ${result.credentialName}`,
    credentialInfo: {
      name: result.credentialName,
      type: result.credentialType,
      source: result.credentialSource
    }
  };
}
```

## Sync Status Handling

### Check Credential Sync Status

Credentials have different sync statuses that affect reliability:

* **`SYNCED`**: Data is current and reliable
* **`SYNCING`**: Data is being updated, may be stale

```javascript theme={null}
async function waitForSync(credId, accessToken, maxWaitMs = 60000) {
  const startTime = Date.now();
  
  while (Date.now() - startTime < maxWaitMs) {
    const info = await getCredentialInfo(credId, accessToken);
    
    if (info.syncStatus === 'SYNCED') {
      return { synced: true, waited: Date.now() - startTime };
    }
    
    await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
  }
  
  return { synced: false, waited: maxWaitMs };
}

// Usage
const syncCheck = await waitForSync('312969464', accessToken);
if (syncCheck.synced) {
  console.log(`Credential synced after ${syncCheck.waited}ms`);
  // Now safe to check eligibility
} else {
  console.log('Credential still syncing, results may be stale');
}
```

## Best Practices

1. **Caching**: Cache eligibility results for 5-10 minutes to reduce API calls
2. **Sync Status**: Check `syncStatus` field before relying on eligibility results
3. **Error Handling**: Always handle network errors and invalid credential IDs
4. **Quest Context**: Use `campaignId` parameter for quest-specific verifications
5. **Rate Limiting**: Implement delays between batch requests (200-500ms)

## Understanding Sync Status

* Always check the `syncStatus` field in responses
* Only rely on results when status is `SYNCED`
* For critical verifications, consider waiting for sync completion
* Cache results appropriately based on sync status

## Next Steps

* **[Quest Integration](/galxe-integration/guides/quests)** - Using credentials in quest requirements
* **[Credential API Reference](/galxe-integration/api-reference/credential)** - Complete API documentation
* **[Authentication](/galxe-integration/getting-started/authentication)** - Access token setup
