Skip to main content

Users API - Complete User Management

The Users API provides comprehensive user management capabilities including profile updates, account verification, TOS acceptance, and PSP integration. Built for enterprise-grade user lifecycle management with KYC compliance, multi-PSP support, and secure authentication flows.

🎯 What Can You Manage?

Your complete user ecosystem with these powerful tools:

  • Profile Management: Update user profiles with OTP verification
  • Account Verification: Email confirmation and account activation
  • KYC Integration: PSP-based identity verification and status updates
  • Terms of Service: TOS acceptance workflow with secure token generation
  • Authentication: User logout and session management
  • OTP System: Secure OTP generation and validation
  • Admin Controls: User listing, detailed profiles, and administrative oversight

🧩 Your User Management Building Blocks

👤 Profile Management Blocks

  • PUT /users/update_profile/:user_id - Update user profile with verification
  • GET /users/:user_id - Get detailed user information
  • GET /users/users - List all users with filtering
  • GET /users/countries - Get supported country list

✅ Account Verification Blocks

  • GET /users/confirm/:token - Confirm user account via email token
  • GET /users/:user_id/tos-acceptance-link - Generate TOS acceptance link
  • POST /users/:user_id/accept-tos - Accept Terms of Service

🔐 Authentication & Security Blocks

  • POST /users/logout - User logout and session termination
  • POST /users/request-otp - Generate OTP for verification
  • POST /users/psp_update - PSP/KYC status updates

🏗️ Common User Management Patterns

Pattern 1: "I want to update my profile securely"

Password verification → OTP generation → Profile update → Activity logging

Use: POST /users/request-otpPUT /users/update_profile/:user_id

Pattern 2: "I want to verify new user accounts"

User registration → Email confirmation → Account activation

Use: GET /users/confirm/:token

Pattern 3: "I want to manage TOS compliance"

Generate TOS link → User acceptance → Update compliance status

Use: GET /users/:user_id/tos-acceptance-linkPOST /users/:user_id/accept-tos

Pattern 4: "I want to process KYC updates"

PSP verification → Status update → User activation

Use: POST /users/psp_update


👤 Profile Management Operations

Update User Profile

PUT/api/users/update_profile/:user_id

Update User Profile with Verification

Update user profile information with current password verification and optional OTP validation. Includes activity logging and secure profile management with rollback support.

Parameters

user_idstringrequired

User ID to update profile for

usernamestring

New username (optional)

emailstring

New email address (optional)

phonestring

New phone number (optional)

current_passwordstringrequired

Current password for verification

otp_codestring

OTP code (required if USE_OTP_CHECK=true)

Request Body

{
  "username": "johndoe_updated",
  "email": "john.doe.new@example.com",
  "phone": "+628123456789",
  "current_password": "currentPassword123",
  "otp_code": "123456"
}

Response

200Profile updated successfully
{
  "message": "Profil berhasil diperbarui",
  "data": {
    "user_id": 123,
    "username": "johndoe_updated",
    "email": "john.doe.new@example.com",
    "phone": "+628123456789",
    "updated": "2024-01-15T10:30:00Z"
  }
}
400Missing required password
{
  "error": "Password wajib diisi untuk memperbarui profil."
}
401Invalid password or OTP
{
  "error": "Password salah"
}
404User not found
{
  "error": "User tidak ditemukan"
}
500Update failed
{
  "error": "Gagal memperbarui profil",
  "details": "Database connection error"
}

Get User by ID

GET/api/users/:user_id

Get Detailed User Information

Retrieve comprehensive user information including profile data, client information, KYC status, and wallet details. Provides complete user overview for admin and user access.

Parameters

user_idstringrequired

User ID to retrieve information for

Response

200User information retrieved successfully
{
  "success": true,
  "data": {
    "user_id": 123,
    "client_id": 456,
    "email": "john.doe@example.com",
    "username": "johndoe",
    "user_status": "ACTIVE",
    "created": "2024-01-01T00:00:00Z",
    "updated": "2024-01-15T10:30:00Z",
    "role": "user",
    "tos_accepted_at": "2024-01-02T08:00:00Z",
    "psp_id": "DOKU_PSP",
    "twitter_username": "johndoe_twitter",
    "twitter_verified": true,
    "twofa_enabled": false,
    "phone": "+628123456789",
    "client_alias": "John Doe Company",
    "client_type": "INDIVIDUAL",
    "client_status": "ACTIVE",
    "country_code": "ID",
    "client_code": "CLI-202401-001",
    "ekyc_status": "APPROVED",
    "ekyc_verified_at": "2024-01-03T12:00:00Z",
    "ekyc_provider": "SUMSUB",
    "ekyc_applicant_id": "APP123456",
    "ekyb_status": "PENDING",
    "ekyb_verified_at": null,
    "ekyb_applicant_id": null,
    "public_key": "PUB-abcd1234efgh5678",
    "profile_picture": "https://storage.example.com/profiles/123.jpg"
  }
}
400Invalid user ID
{
  "error": "user_id tidak valid."
}
404User not found
{
  "success": false,
  "error": "User tidak ditemukan."
}
500Failed to retrieve user data
{
  "success": false,
  "error": "Gagal mengambil data user."
}

Get All Users

GET/api/users/users

Get All Users with Statistics

Retrieve list of all users with transaction statistics, wallet balances, and filtering options. Includes comprehensive user analytics and volume data.

Parameters

client_idnumber

Filter by specific client ID (optional)

Response

200Users list retrieved successfully
[
  {
    "user_id": 123,
    "email": "john.doe@example.com",
    "username": "johndoe",
    "client_id": 456,
    "user_status": "ACTIVE",
    "ekyc_status": "APPROVED",
    "transactions_count": 45,
    "total_volume": "15750.00 USD",
    "total_balance": "2500.50 USD"
  },
  {
    "user_id": 124,
    "email": "jane.smith@example.com",
    "username": "janesmith",
    "client_id": 456,
    "user_status": "ACTIVE",
    "ekyc_status": "PENDING",
    "transactions_count": 12,
    "total_volume": "3200.00 USD",
    "total_balance": "850.25 USD"
  }
]
500Failed to fetch users
{
  "error": "Terjadi kesalahan server"
}

Get Country List

GET/api/users/countries

Get Supported Countries List

Retrieve list of supported countries from PSP providers with FIAT_PSP type. Returns country codes with mapped country names for user registration and country-specific configuration.

Response

200Countries list retrieved successfully
{
  "countries": [
    {
      "country_code": "AU",
      "country_name": "Australia"
    },
    {
      "country_code": "ID",
      "country_name": "Indonesia"
    },
    {
      "country_code": "IN",
      "country_name": "India"
    },
    {
      "country_code": "SG",
      "country_name": "Singapore"
    },
    {
      "country_code": "US",
      "country_name": "United States"
    }
  ]
}
500Failed to fetch countries
{
  "error": "Failed to fetch country list."
}

✅ Account Verification Operations

Confirm User Account

GET/api/users/confirm/:token

Confirm User Account via Email Token

Activate user account using confirmation token sent via email. Automatically activates user status and removes confirmation token upon successful verification.

Parameters

tokenstringrequired

Email confirmation token (32-character hex string)

Response

200Account confirmed successfully
"\n      <html>\n        <head><title>Konfirmasi Berhasil</title></head>\n        <body>\n          <h2>Akun Anda telah dikonfirmasi!</h2>\n          <p>Silakan login untuk mengakses akun Anda.</p>\n          <a href=\"undefined/login\">Ke Halaman Login</a>\n        </body>\n      </html>\n    "
400Invalid or expired token
"\n      <html>\n        <head><title>Konfirmasi Gagal</title></head>\n        <body>\n          <h2>Token tidak valid atau sudah kedaluwarsa.</h2>\n          <p>Silakan daftar ulang atau hubungi support.</p>\n          <a href=\"undefined/login\">Ke Halaman Login</a>\n        </body>\n      </html>\n    "
500Confirmation failed
"\n      <html>\n        <head><title>Error</title></head>\n        <body>\n          <h2>Terjadi kesalahan saat konfirmasi akun.</h2>\n          <p>Silakan coba lagi nanti atau hubungi support.</p>\n          <a href=\"undefined/login\">Ke Halaman Login</a>\n        </body>\n      </html>\n    "
GET/api/users/:user_id/tos-acceptance-link

Generate Terms of Service Acceptance Link

Generate secure link for Terms of Service acceptance with time-limited token. Used for compliance management and legal agreement workflows.

Parameters

user_idstringrequired

User ID to generate TOS link for

Response

200TOS acceptance link generated successfully
{
  "message": "TOS acceptance link generated successfully.",
  "link": "https://frontend.example.com/accept-terms-of-service?email=john.doe@example.com&t=abc123def456..."
}
400Invalid user ID format
{
  "error": "Invalid user_id format."
}
404User not found
{
  "error": "User not found."
}
500Failed to generate link
{
  "error": "Failed to generate TOS acceptance link."
}

Accept Terms of Service

POST/api/users/:user_id/accept-tos

Accept Terms of Service

Accept Terms of Service using secure token and update user compliance status. Validates token expiry and updates acceptance timestamp.

Parameters

user_idstringrequired

User ID accepting TOS

tokenstringrequired

TOS acceptance token from generated link

Request Body

{
  "token": "abc123def456ghi789jkl012mno345pqr"
}

Response

200TOS accepted successfully
{
  "message": "TOS accepted successfully.",
  "user_id": 123,
  "tos_accepted_at": "2024-01-15T10:30:00Z"
}
400Invalid request or token
{
  "error": "Invalid token or user_id."
}
403Token expired
{
  "error": "Token expired."
}
500Failed to accept TOS
{
  "error": "Failed to accept TOS."
}

🔐 Authentication & Security Operations

User Logout

POST/api/users/logout

User Logout and Session Termination

Logout user and terminate active session. Stateless logout for JWT-based authentication with activity logging.

Request Body

{}

Response

200Logout successful
{
  "message": "Logout successful.",
  "user_id": 123,
  "timestamp": "2024-01-15T10:30:00Z"
}
500Logout failed
{
  "error": "Logout failed",
  "details": "Session termination error"
}

Request OTP

POST/api/users/request-otp

Generate OTP for Verification

Generate One-Time Password for user verification. OTP expires in 5 minutes and is sent via WhatsApp in production mode. Returns OTP in response for development/mock mode.

Parameters

user_idnumberrequired

User ID to generate OTP for

Request Body

{
  "user_id": 123
}

Response

200OTP generated and sent successfully
{
  "message": "OTP berhasil dikirim.",
  "otp": "123456"
}
400Invalid user ID
{
  "error": "user_id harus disertakan dan berupa angka."
}
404User not found
{
  "error": "User tidak ditemukan."
}
500Failed to generate OTP
{
  "error": "Gagal generate OTP.",
  "details": "Database connection error"
}

PSP Update

POST/api/users/psp_update

PSP/KYC Status Update

Update user KYC status and information from PSP providers. Automatically activates user account when KYC is approved and includes activity logging.

Parameters

user_idnumberrequired

User ID to update

ekyc_statusstringrequired

KYC verification status (PENDING, APPROVED, REJECTED)

ekyc_verified_atstringrequired

KYC verification timestamp (ISO 8601)

country_codestringrequired

Country code for compliance

Request Body

{
  "user_id": 123,
  "ekyc_status": "APPROVED",
  "ekyc_verified_at": "2024-01-15T10:30:00Z",
  "country_code": "ID"
}

Response

200PSP update successful
{
  "message": "User eKYC status updated successfully"
}
400Missing required parameters
{
  "error": "user_id, ekyc_status, ekyc_verified_at, and country_code are required."
}
500Update failed
{
  "error": "Failed to update user",
  "details": "Database connection error"
}

🚀 Complete User Management Workflows

Workflow 1: Secure Profile Update

// Complete secure profile update workflow
async function secureProfileUpdate(userId, profileData, currentPassword) {
console.log('Starting secure profile update...');

// Step 1: Request OTP for verification
const otpRequest = await fetch('/api/users/request-otp', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ user_id: userId })
});

console.log('OTP sent for verification');

// Step 2: Get OTP from user (implementation depends on your UI)
const otpCode = await getUserOTPInput(); // Your implementation

// Step 3: Update profile with verification
const updateResult = await fetch(`/api/users/update_profile/${userId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${userToken}`
},
body: JSON.stringify({
...profileData,
current_password: currentPassword,
otp_code: otpCode
})
});

console.log('Profile updated successfully');
return updateResult.json();
}

Workflow 2: Account Verification Flow

// Complete account verification workflow
async function verifyUserAccount(confirmationToken) {
console.log('Processing account verification...');

// Direct GET request to confirmation endpoint
// This will return HTML response for user display
const response = await fetch(`/api/users/confirm/${confirmationToken}`, {
method: 'GET'
});

if (response.ok) {
console.log('Account verified successfully');
// Redirect user to login page
window.location.href = `${process.env.FRONTEND_URL}/login`;
} else {
console.log('Verification failed');
// Handle verification failure
}

return response;
}

Workflow 3: TOS Compliance Management

// Complete TOS compliance workflow
async function manageTOSCompliance(userId) {
console.log('Managing TOS compliance...');

// Step 1: Generate TOS acceptance link
const linkResponse = await fetch(`/api/users/${userId}/tos-acceptance-link`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${userToken}`
}
});

const linkData = await linkResponse.json();
console.log(`TOS link generated: ${linkData.link}`);

// Step 2: Send link to user (email, notification, etc.)
await sendTOSLinkToUser(linkData.link);

// Step 3: User accepts TOS (this would be called from the TOS acceptance page)
// const acceptanceResult = await fetch(`/api/users/${userId}/accept-tos`, {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify({ token: tosToken })
// });

return linkData;
}

Workflow 4: KYC Status Processing

// Complete KYC status processing workflow (PSP side)
async function processKYCUpdate(userKYCData) {
console.log('Processing KYC status update...');

const { user_id, kyc_status, verification_date, country } = userKYCData;

// Step 1: Validate KYC data
if (!user_id || !kyc_status || !verification_date || !country) {
throw new Error('Missing required KYC data');
}

// Step 2: Update user KYC status
const updateResult = await fetch('/api/users/psp_update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': pspApiKey
},
body: JSON.stringify({
user_id: user_id,
ekyc_status: kyc_status,
ekyc_verified_at: verification_date,
country_code: country
})
});

if (updateResult.ok) {
console.log(`KYC status updated for user ${user_id}: ${kyc_status}`);

// Step 3: If approved, user is automatically activated
if (kyc_status === 'APPROVED') {
console.log(`User ${user_id} automatically activated due to KYC approval`);
}
}

return updateResult.json();
}

Workflow 5: Admin User Management

// Complete admin user management workflow
async function adminUserManagement(filters = {}) {
console.log('Starting admin user management...');

// Step 1: Get all users with statistics
const usersResponse = await fetch('/api/users/users?' + new URLSearchParams(filters), {
headers: {
'Authorization': `Bearer ${adminToken}`,
'x-api-key': apiKey
}
});

const users = await usersResponse.json();
console.log(`Found ${users.length} users`);

// Step 2: Get detailed information for specific users
const detailedUsers = await Promise.all(
users.slice(0, 10).map(async (user) => {
const detailResponse = await fetch(`/api/users/${user.user_id}`, {
headers: {
'Authorization': `Bearer ${adminToken}`
}
});
return detailResponse.json();
})
);

// Step 3: Generate analytics
const analytics = {
total_users: users.length,
active_users: users.filter(u => u.user_status === 'ACTIVE').length,
kyc_approved: users.filter(u => u.ekyc_status === 'APPROVED').length,
total_volume: users.reduce((sum, u) => sum + parseFloat(u.total_volume.replace(' USD', '')), 0),
average_transactions: users.reduce((sum, u) => sum + u.transactions_count, 0) / users.length
};

console.log('User analytics:', analytics);

return {
users,
detailed_users: detailedUsers,
analytics
};
}

🎯 When to Use Which API

Profile Management

  • Update profile: PUT /users/update_profile/:user_id
  • View profile: GET /users/:user_id
  • List users: GET /users/users
  • Country setup: GET /users/countries

Account Verification

  • Email confirmation: GET /users/confirm/:token
  • Account activation: Automatic via confirmation

Compliance Management

  • TOS workflow: GET /users/:user_id/tos-acceptance-linkPOST /users/:user_id/accept-tos
  • KYC updates: POST /users/psp_update

Security Operations

  • Profile security: POST /users/request-otpPUT /users/update_profile/:user_id
  • Session management: POST /users/logout

💡 Pro Tips for User Management

Security Best Practices

// Always validate current password for profile updates
const validateProfileUpdate = async (userId, currentPassword, newData) => {
// Step 1: Verify current password
if (!currentPassword) {
throw new Error('Current password required for profile updates');
}

// Step 2: Use OTP if enabled
if (process.env.USE_OTP_CHECK === 'true') {
const otp = await requestOTP(userId);
// Wait for user OTP input
newData.otp_code = await getUserOTPInput();
}

// Step 3: Execute update with verification
return updateProfile(userId, { ...newData, current_password: currentPassword });
};

Error Handling

// Robust error handling for user operations
async function safeUserOperation(operation) {
try {
return await operation();
} catch (error) {
if (error.message.includes('Password salah')) {
return { error: 'invalid_password', suggestion: 'verify_current_password' };
} else if (error.message.includes('Invalid or expired OTP')) {
return { error: 'invalid_otp', suggestion: 'request_new_otp' };
} else if (error.message.includes('Token expired')) {
return { error: 'token_expired', suggestion: 'regenerate_link' };
} else if (error.message.includes('User tidak ditemukan')) {
return { error: 'user_not_found', suggestion: 'verify_user_id' };
} else {
console.error('Unexpected user error:', error.message);
throw error;
}
}
}

OTP Configuration

// Environment-based OTP handling
class OTPManager {
static async requestOTP(userId) {
const response = await fetch('/api/users/request-otp', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ user_id: userId })
});

const result = await response.json();

// In MOCK_MODE, OTP is returned in response
if (result.otp) {
console.log(`Development OTP: ${result.otp}`);
return result.otp;
}

// In production, OTP is sent via WhatsApp
console.log('OTP sent via WhatsApp');
return null; // User must enter OTP manually
}

static isOTPRequired() {
return process.env.USE_OTP_CHECK === 'true';
}
}

TOS Management

// TOS compliance tracking
class TOSManager {
static async generateTOSLink(userId) {
const response = await fetch(`/api/users/${userId}/tos-acceptance-link`, {
headers: { 'Authorization': `Bearer ${token}` }
});
return response.json();
}

static async acceptTOS(userId, token) {
const response = await fetch(`/api/users/${userId}/accept-tos`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token })
});
return response.json();
}

static extractTokenFromURL(url) {
const urlParams = new URLSearchParams(new URL(url).search);
return urlParams.get('t');
}
}

🔐 Authentication & Authorization

Different endpoints require different authentication levels:

// Public endpoints (no auth required)
await fetch('/api/users/countries');
await fetch('/api/users/confirm/token123');

// User-level endpoints (JWT token required)
await fetch('/api/users/123', {
headers: { 'Authorization': `Bearer ${userToken}` }
});

// Admin-level endpoints (admin role required)
await fetch('/api/users/users', {
headers: {
'Authorization': `Bearer ${adminToken}`,
'x-api-key': apiKey
}
});

// PSP endpoints (PSP role required)
await fetch('/api/users/psp_update', {
headers: { 'x-api-key': pspApiKey }
});

🌍 Environment Configuration

Required Environment Variables

# OTP Configuration
USE_OTP_CHECK=true # Enable/disable OTP verification
MOCK_MODE=false # Development mode settings

# Frontend URLs
FRONTEND_URL=https://app.example.com # Main application URL
TOS_FRONTEND_URL=https://legal.example.com # TOS acceptance page

# Email Service
APP_URL=https://api.example.com # API base URL for email links

Feature Flags

  • USE_OTP_CHECK: Controls OTP requirement for profile updates
  • MOCK_MODE: Returns OTP in response for development
  • Email Confirmation: Automatic email sending for account verification
  • TOS Compliance: Secure token-based Terms of Service acceptance

📊 Response Format Standards

All endpoints follow consistent response formats:

Success Responses

{
"success": true,
"data": { ... },
"message": "Operation completed successfully"
}

Error Responses

{
"success": false,
"error": "Error description",
"details": "Additional error details"
}

HTML Responses (Confirmation Pages)

Account confirmation endpoints return formatted HTML for user-friendly display in browsers.


Profile Update Security

Always require current password verification for profile updates. Use OTP verification in production environments for additional security. Activity logging tracks all profile changes for audit purposes.

Token Expiry

Email confirmation tokens and TOS acceptance tokens expire after 24 hours. Implement proper error handling for expired tokens and provide regeneration options for users.

KYC Integration

PSP updates automatically activate user accounts when KYC status is APPROVED. This triggers wallet currency updates and removes user restrictions based on verification status.