Skip to main content
GET
/
api
/
v2.1
/
customer
/
individual
/
categorization
/
hierarchy
/
{tenantId}
Get categorization hierarchy
curl --request GET \
  --url https://sandbox.finhub.cloud/api/v2.1/customer/individual/categorization/hierarchy/{tenantId} \
  --header 'Authorization: <authorization>' \
  --header 'User-Agent: <user-agent>' \
  --header 'X-Forwarded-For: <x-forwarded-for>' \
  --header 'X-Forwarded-From: <x-forwarded-from>' \
  --header 'X-Tenant-ID: <x-tenant-id>' \
  --header 'deviceId: <deviceid>' \
  --header 'platform: <platform>'
{
  "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)Example: 97e7ff29-15f3-49ef-9681-3bbfcce4f6cd
Accept
string
Response format (optional — defaults to application/json)Example: application/json, text/plain, */*
X-Forwarded-From
string
required
Source identifier for request origin trackingExample: e2e-test
User-Agent
string
required
Client application identifier — required by the global request filterExample: YourApp/1.0 or Mozilla/5.0 (Windows NT 10.0; Win64; x64)
platform
string
required
Client platform identifier. Also accepted as sec-ch-ua-platformExample: web
deviceId
string
required
Unique device identifier for session tracking. Also accepted as X-Device-Id or device-idExample: 356938035643809

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: 97e7ff29-15f3-49ef-9681-3bbfcce4f6cd" \
  -H "X-Forwarded-From: e2e-test" \
  -H "User-Agent: YourApp/1.0" \
  -H "platform: web" \
  -H "deviceId: 356938035643809"
{
  "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

Headers

User-Agent
string
required

Browser user agent

Example:

"Mozilla/5.0"

X-Forwarded-For
string
required

Client/application Ip address

Example:

"192.168.0.1"

X-Forwarded-From
string
required

Client/application identifier for request source tracking

Example:

"playground"

X-Tenant-ID
string
required

Tenant identifier

Example:

"97e7ff29-15f3-49ef-9681-3bbfcce4f6cd"

platform
string
required

Client platform identifier. Also accepted as sec-ch-ua-platform

Example:

"web"

deviceId
string
required

Device identifier

Example:

"e2e-test-device"

Authorization
string
required

Bearer token from admin or customer session creation

Example:

"Bearer <token>"

Path Parameters

tenantId
string
required

Tenant identifier to retrieve hierarchy for

Example:

"97e7ff29-15f3-49ef-9681-3bbfcce4f6cd"

Response

200

OK