Skip to main content
GET
https://sandbox.finhub.cloud
/
api
/
v2.1
/
customer
/
individual
/
categorization
/
hierarchy
/
{tenantId}
Customer Categorization API
curl --request GET \
  --url https://sandbox.finhub.cloud/api/v2.1/customer/individual/categorization/hierarchy/{tenantId} \
  --header 'Authorization: <authorization>' \
  --header 'X-Tenant-ID: <x-tenant-id>'
{
  "tenantId": "d1e2f3a4-b5c6-47d8-9e0f-1a2b3c4d5e6f",
  "tenantName": "CloudVault",
  "complianceLevel": "NON_REGULATED",
  "categories": {
    "INDIVIDUAL_BASIC": {
      "databaseId": "cat-cloudvault-ind-001",
      "categoryId": "INDIVIDUAL_BASIC",
      "categoryName": "Individual Basic",
      "availableFeatures": []
    },
    "INDIVIDUAL_VERIFIED": {
      "databaseId": "cat-cloudvault-ind-002",
      "categoryId": "INDIVIDUAL_VERIFIED",
      "categoryName": "Individual Verified",
      "availableFeatures": []
    },
    "INDIVIDUAL_PREMIUM": {
      "databaseId": "cat-cloudvault-ind-003",
      "categoryId": "INDIVIDUAL_PREMIUM",
      "categoryName": "Individual Premium",
      "availableFeatures": []
    },
    "ENTERPRISE": {
      "databaseId": "cat-cloudvault-003",
      "categoryId": "ENTERPRISE",
      "categoryName": "Enterprise",
      "availableFeatures": []
    }
  },
  "features": {
    "CUSTOMER_PROFILE": {
      "databaseId": "feat-payflow-profile-001",
      "featureCode": "CUSTOMER_PROFILE",
      "featureName": "Customer Profile",
      "mandatoryKeys": [
        "data_retention_days",
        "crypto_exchange_integration",
        "industry",
        "profile_completeness",
        "payment_gateway_integration",
        "data_source"
      ],
      "allowedValues": {
        "industry": ["OTHER", "FINTECH", "PAYMENT_SERVICES", "ECOMMERCE"],
        "profile_completeness": ["BASIC", "STANDARD", "FULL"],
        "payment_gateway_integration": ["true", "false"],
        "crypto_exchange_integration": ["true", "false"],
        "data_source": ["external", "banq_platform"]
      },
      "multiValueKeys": []
    },
    "KYC_VERIFICATION": {
      "databaseId": "feat-payflow-kyc-001",
      "featureCode": "KYC_VERIFICATION",
      "featureName": "KYC Verification",
      "mandatoryKeys": [
        "verification_level",
        "enhanced_verification",
        "verification_performed_by",
        "document_types",
        "crypto_specific_kyc"
      ],
      "allowedValues": {
        "verification_level": ["BASIC", "STANDARD", "ENHANCED"],
        "enhanced_verification": ["true", "false"],
        "verification_performed_by": ["external", "banq"],
        "document_types": ["PASSPORT", "NATIONAL_ID", "PROOF_OF_ADDRESS", "BUSINESS_LICENSE", "FINANCIAL_STATEMENTS"],
        "crypto_specific_kyc": ["true", "false"]
      },
      "multiValueKeys": ["document_types"]
    },
    "SECURITY_FEATURES": {
      "databaseId": "feat-cloudvault-security-001",
      "featureCode": "SECURITY_FEATURES",
      "featureName": "Security Features",
      "mandatoryKeys": ["encryption_level", "two_factor_auth"],
      "allowedValues": {
        "encryption_level": ["AES128", "AES256", "AES512"],
        "two_factor_auth": ["OPTIONAL", "REQUIRED"]
      },
      "multiValueKeys": []
    },
    "STORAGE_MANAGEMENT": {
      "databaseId": "feat-cloudvault-storage-001",
      "featureCode": "STORAGE_MANAGEMENT",
      "featureName": "Storage Management",
      "mandatoryKeys": ["storage_quota_gb", "backup_frequency"],
      "allowedValues": {
        "storage_quota_gb": ["50", "100", "500", "1000", "UNLIMITED"],
        "backup_frequency": ["HOURLY", "DAILY", "WEEKLY"]
      },
      "multiValueKeys": []
    },
    "DOCUMENT_MANAGEMENT": {
      "databaseId": "feat-payflow-docs-001",
      "featureCode": "DOCUMENT_MANAGEMENT",
      "featureName": "Document Management",
      "mandatoryKeys": [],
      "allowedValues": {},
      "multiValueKeys": []
    }
  }
}

Customer Categorization API

APIs for managing customer categorization hierarchy, validation, and refresh operations.
Base URL: https://sandbox.finhub.cloud/api/v2.1/customer/categorization

Available Operations

Main Operation: Get Categorization HierarchyRetrieves the complete categorization hierarchy including all categories, features, and their configurations for a tenant.

Get Categorization Hierarchy

Retrieves the complete categorization hierarchy for a tenant.
Required First Step: You MUST call this endpoint BEFORE customer registration to retrieve valid categories, features, and their constraints.The response provides:
  • Available customer categories (INDIVIDUAL_BASIC, INDIVIDUAL_VERIFIED, etc.)
  • Feature definitions with mandatory keys
  • Allowed values for each feature parameter
  • Multi-value key specifications

Request

For complete details on authentication and headers, refer to the Standard HTTP Headers reference documentation.
tenantId
string
required
Tenant identifier
Authorization
string
required
Bearer token for authentication
X-Tenant-ID
string
required
Tenant identifier (must match path parameter)
Content-Type
string
Response format: application/json

Code Examples

curl -X GET "https://sandbox.finhub.cloud/api/v2.1/customer/individual/categorization/hierarchy/d1e2f3a4-b5c6-47d8-9e0f-1a2b3c4d5e6f" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "X-Tenant-ID: tenant_cloudvault"
{
  "tenantId": "d1e2f3a4-b5c6-47d8-9e0f-1a2b3c4d5e6f",
  "tenantName": "CloudVault",
  "complianceLevel": "NON_REGULATED",
  "categories": {
    "INDIVIDUAL_BASIC": {
      "databaseId": "cat-cloudvault-ind-001",
      "categoryId": "INDIVIDUAL_BASIC",
      "categoryName": "Individual Basic",
      "availableFeatures": []
    },
    "INDIVIDUAL_VERIFIED": {
      "databaseId": "cat-cloudvault-ind-002",
      "categoryId": "INDIVIDUAL_VERIFIED",
      "categoryName": "Individual Verified",
      "availableFeatures": []
    },
    "INDIVIDUAL_PREMIUM": {
      "databaseId": "cat-cloudvault-ind-003",
      "categoryId": "INDIVIDUAL_PREMIUM",
      "categoryName": "Individual Premium",
      "availableFeatures": []
    },
    "ENTERPRISE": {
      "databaseId": "cat-cloudvault-003",
      "categoryId": "ENTERPRISE",
      "categoryName": "Enterprise",
      "availableFeatures": []
    }
  },
  "features": {
    "CUSTOMER_PROFILE": {
      "databaseId": "feat-payflow-profile-001",
      "featureCode": "CUSTOMER_PROFILE",
      "featureName": "Customer Profile",
      "mandatoryKeys": [
        "data_retention_days",
        "crypto_exchange_integration",
        "industry",
        "profile_completeness",
        "payment_gateway_integration",
        "data_source"
      ],
      "allowedValues": {
        "industry": ["OTHER", "FINTECH", "PAYMENT_SERVICES", "ECOMMERCE"],
        "profile_completeness": ["BASIC", "STANDARD", "FULL"],
        "payment_gateway_integration": ["true", "false"],
        "crypto_exchange_integration": ["true", "false"],
        "data_source": ["external", "banq_platform"]
      },
      "multiValueKeys": []
    },
    "KYC_VERIFICATION": {
      "databaseId": "feat-payflow-kyc-001",
      "featureCode": "KYC_VERIFICATION",
      "featureName": "KYC Verification",
      "mandatoryKeys": [
        "verification_level",
        "enhanced_verification",
        "verification_performed_by",
        "document_types",
        "crypto_specific_kyc"
      ],
      "allowedValues": {
        "verification_level": ["BASIC", "STANDARD", "ENHANCED"],
        "enhanced_verification": ["true", "false"],
        "verification_performed_by": ["external", "banq"],
        "document_types": ["PASSPORT", "NATIONAL_ID", "PROOF_OF_ADDRESS", "BUSINESS_LICENSE", "FINANCIAL_STATEMENTS"],
        "crypto_specific_kyc": ["true", "false"]
      },
      "multiValueKeys": ["document_types"]
    },
    "SECURITY_FEATURES": {
      "databaseId": "feat-cloudvault-security-001",
      "featureCode": "SECURITY_FEATURES",
      "featureName": "Security Features",
      "mandatoryKeys": ["encryption_level", "two_factor_auth"],
      "allowedValues": {
        "encryption_level": ["AES128", "AES256", "AES512"],
        "two_factor_auth": ["OPTIONAL", "REQUIRED"]
      },
      "multiValueKeys": []
    },
    "STORAGE_MANAGEMENT": {
      "databaseId": "feat-cloudvault-storage-001",
      "featureCode": "STORAGE_MANAGEMENT",
      "featureName": "Storage Management",
      "mandatoryKeys": ["storage_quota_gb", "backup_frequency"],
      "allowedValues": {
        "storage_quota_gb": ["50", "100", "500", "1000", "UNLIMITED"],
        "backup_frequency": ["HOURLY", "DAILY", "WEEKLY"]
      },
      "multiValueKeys": []
    },
    "DOCUMENT_MANAGEMENT": {
      "databaseId": "feat-payflow-docs-001",
      "featureCode": "DOCUMENT_MANAGEMENT",
      "featureName": "Document Management",
      "mandatoryKeys": [],
      "allowedValues": {},
      "multiValueKeys": []
    }
  }
}

Smart Value Selection Patterns

When building categoryFeatureRelations for registration, use smart value selection to choose risk-appropriate values from allowedValues. This pattern is used in production for sophisticated risk management.

Why Smart Selection Matters

Instead of using the first value in allowedValues, production systems analyze the key name and select appropriate values based on:
  • Customer risk profile (low, medium, high)
  • Business requirements
  • Compliance needs
  • Transaction patterns

Selection Logic by Risk Level

Implementation Example

/**
 * Smart value selection for medium-risk customers
 * Production-proven pattern from PowerShell automation
 */
function selectSmartValue(key, allowedValues, riskLevel = 'MEDIUM') {
  const keyLower = key.toLowerCase();
  let value = null;
  
  if (!allowedValues || allowedValues.length === 0) {
    // Provide smart defaults when no allowed values
    return getSmartDefault(keyLower, riskLevel);
  }
  
  // Risk level parameters
  if (keyLower.includes('risk_level') || keyLower.includes('level_risk')) {
    if (riskLevel === 'MEDIUM') {
      value = allowedValues.find(v => v.match(/MEDIUM|STANDARD|NORMAL/i));
      if (!value && allowedValues.length > 1) {
        value = allowedValues[Math.floor(allowedValues.length / 2)]; // Middle value
      }
    } else if (riskLevel === 'LOW') {
      value = allowedValues.find(v => v.match(/LOW|BASIC|MINIMAL/i));
    } else if (riskLevel === 'HIGH') {
      value = allowedValues.find(v => v.match(/HIGH|ENHANCED|MAXIMUM/i));
    }
  }
  
  // Risk score (numeric)
  else if (keyLower.includes('risk_score') || keyLower.includes('score_risk')) {
    if (riskLevel === 'MEDIUM') {
      value = allowedValues.find(v => {
        const num = parseInt(v);
        return num >= 40 && num <= 69;
      });
      if (!value) value = '55'; // Default medium score
    } else if (riskLevel === 'LOW') {
      value = allowedValues.find(v => parseInt(v) < 40);
      if (!value) value = '25';
    } else if (riskLevel === 'HIGH') {
      value = allowedValues.find(v => parseInt(v) >= 70);
      if (!value) value = '85';
    }
  }
  
  // PEP (Politically Exposed Person)
  else if (keyLower.includes('pep')) {
    value = allowedValues.find(v => v.match(/FALSE|NO|NOT_APPLICABLE/i));
    if (!value) value = 'false';
  }
  
  // Sanctions check frequency
  else if (keyLower.includes('sanctions')) {
    if (riskLevel === 'MEDIUM') {
      value = allowedValues.find(v => v.match(/STANDARD|PERIODIC|DAILY/i));
    } else if (riskLevel === 'HIGH') {
      value = allowedValues.find(v => v.match(/ENHANCED|DAILY|CONTINUOUS/i));
    }
    if (!value) value = 'STANDARD';
  }
  
  // Monitoring frequency
  else if (keyLower.includes('monitoring') || keyLower.includes('frequency')) {
    if (riskLevel === 'MEDIUM') {
      value = allowedValues.find(v => v.match(/WEEKLY|STANDARD|PERIODIC/i));
      if (!value) value = 'WEEKLY';
    } else if (riskLevel === 'LOW') {
      value = allowedValues.find(v => v.match(/MONTHLY|QUARTERLY/i));
      if (!value) value = 'MONTHLY';
    } else if (riskLevel === 'HIGH') {
      value = allowedValues.find(v => v.match(/DAILY|CONTINUOUS/i));
      if (!value) value = 'DAILY';
    }
  }
  
  // Transaction limits
  else if (keyLower.includes('monthly_limit') || keyLower.includes('limit_monthly')) {
    if (riskLevel === 'MEDIUM') {
      value = allowedValues.find(v => {
        const num = parseInt(v);
        return num >= 15000 && num <= 25000;
      });
      if (!value) value = '20000';
    } else if (riskLevel === 'LOW') {
      value = allowedValues.find(v => parseInt(v) <= 10000);
      if (!value) value = '5000';
    } else if (riskLevel === 'HIGH') {
      value = allowedValues.find(v => parseInt(v) >= 50000 || v === 'UNLIMITED');
      if (!value) value = '100000';
    }
  }
  
  else if (keyLower.includes('daily_limit') || keyLower.includes('limit_daily')) {
    if (riskLevel === 'MEDIUM') {
      value = allowedValues.find(v => {
        const num = parseInt(v);
        return num >= 3000 && num <= 7000;
      });
      if (!value) value = '5000';
    }
  }
  
  // Enhanced Due Diligence
  else if (keyLower.includes('edd') || keyLower.includes('enhanced_due_diligence')) {
    if (riskLevel === 'HIGH') {
      value = allowedValues.find(v => v.match(/REQUIRED|TRUE|YES/i));
    } else {
      value = allowedValues.find(v => v.match(/OPTIONAL|FALSE|NO/i));
    }
    if (!value) value = riskLevel === 'HIGH' ? 'REQUIRED' : 'OPTIONAL';
  }
  
  // Verification level
  else if (keyLower.includes('verification_level')) {
    if (riskLevel === 'MEDIUM') {
      value = allowedValues.find(v => v.match(/STANDARD|VERIFIED/i));
    } else if (riskLevel === 'LOW') {
      value = allowedValues.find(v => v.match(/BASIC|MINIMAL/i));
    } else if (riskLevel === 'HIGH') {
      value = allowedValues.find(v => v.match(/ENHANCED|FULL/i));
    }
    if (!value) value = 'STANDARD';
  }
  
  // Default: use first value
  if (!value) {
    value = allowedValues[0];
  }
  
  return value;
}

/**
 * Provide smart defaults when allowedValues is empty
 */
function getSmartDefault(keyLower, riskLevel) {
  if (keyLower.includes('risk_level')) {
    return riskLevel;
  }
  if (keyLower.includes('risk_score')) {
    return riskLevel === 'LOW' ? '25' : riskLevel === 'MEDIUM' ? '55' : '85';
  }
  if (keyLower.includes('pep')) {
    return 'false';
  }
  if (keyLower.includes('sanctions')) {
    return 'STANDARD';
  }
  if (keyLower.includes('monitoring') || keyLower.includes('frequency')) {
    return riskLevel === 'MEDIUM' ? 'WEEKLY' : riskLevel === 'LOW' ? 'MONTHLY' : 'DAILY';
  }
  if (keyLower.includes('monthly_limit')) {
    return riskLevel === 'MEDIUM' ? '20000' : riskLevel === 'LOW' ? '5000' : '100000';
  }
  if (keyLower.includes('verification_level')) {
    return riskLevel === 'LOW' ? 'BASIC' : riskLevel === 'MEDIUM' ? 'STANDARD' : 'ENHANCED';
  }
  return 'STANDARD'; // Safe default
}

/**
 * Build complete categoryFeatureRelations with smart selection
 */
function buildSmartCategoryFeatureRelations(category, riskLevel = 'MEDIUM') {
  const relations = [];
  
  if (!category.availableFeatures) {
    return relations;
  }
  
  for (const feature of category.availableFeatures) {
    const parametrization = [];
    
    if (feature.mandatoryKeys) {
      for (const key of feature.mandatoryKeys) {
        const allowedValues = feature.allowedValues?.[key] || [];
        const value = selectSmartValue(key, allowedValues, riskLevel);
        
        parametrization.push({
          name: key,
          value: value
        });
      }
    }
    
    relations.push({
      feature: {
        id: feature.databaseId,
        code: feature.featureCode
      },
      enabled: true,
      parametrization
    });
  }
  
  return relations;
}

// Usage Example
const hierarchy = await getCategorizationHierarchy(tenantId);
const category = hierarchy.categories['INDIVIDUAL_VERIFIED'];
const categoryFeatureRelations = buildSmartCategoryFeatureRelations(category, 'MEDIUM');
Production Pattern: This smart selection logic is used in production automation scripts and has been proven to handle various category configurations reliably. It provides sensible defaults based on risk level while respecting allowed values when present.

Risk Levels

LevelDescriptionTypical Use
LOWLow risk customersStandard retail, low volumes
MEDIUMMedium risk customersRegular users, moderate volumes
HIGHHigh risk customersHigh-value, requires EDD
PROHIBITEDProhibited categoriesCannot onboard

Verification Levels

LevelDescriptionRequirements
BASICBasic verificationGovernment ID only
STANDARDStandard verificationID + Proof of Address
ENHANCEDEnhanced due diligenceID + Address + Source of Funds

How to Use Categorization

1

Call Before Registration

Always call this endpoint BEFORE customer registration to get valid category IDs and feature codes
2

Extract Category Information

Get the databaseId from the category you want to assign (e.g., INDIVIDUAL_VERIFIED)
3

Build Feature Parametrization

For each feature, provide all mandatoryKeys with values from allowedValues
4

Include in Registration

Pass the categorization object in your registration request
Example: Building Categorization for Registration
// 1. Get hierarchy
const hierarchy = await getCategorizationHierarchy(tenantId);

// 2. Select category
const category = hierarchy.categories['INDIVIDUAL_VERIFIED'];

// 3. Build categorization object
const categorization = {
  id: category.databaseId,
  name: category.categoryName,
  categoryFeatureRelations: [
    {
      feature: {
        id: hierarchy.features['KYC_VERIFICATION'].databaseId,
        code: 'KYC_VERIFICATION'
      },
      enabled: true,
      parametrization: [
        { name: 'verification_level', value: 'STANDARD' },
        { name: 'enhanced_verification', value: 'false' },
        { name: 'verification_performed_by', value: 'banq' },
        { name: 'document_types', value: ['PASSPORT', 'PROOF_OF_ADDRESS'] },
        { name: 'crypto_specific_kyc', value: 'false' }
      ]
    }
  ]
};

// 4. Use in registration
await registerCustomer({ ...customerData, categorization });

API Schema Reference

For the complete OpenAPI schema specification, see the API Schema Mapping document.

Response Codes

CodeDescription
200Operation successful
400Invalid request data
401Not Authorized
403Not Allowed - Admin role required for refresh
404Tenant not found
500Internal server error