Client API
Client management operations including client creation with admin users, instance management, and organizational structure. Handles PSP assignment, eKYC integration, Xendit customer creation for Indonesia, and email notifications.
Create Client with Admin
Create Client with Admin
Create a new client organization with an admin user. Automatically generates client code (CL-YYYYMM-XXXX format), assigns PSP based on country, creates admin user with secure password, and sends welcome email. Includes Xendit customer creation for Indonesia clients. Uses database transaction safety.
Parameters
emailstringrequiredAdmin email address (must be unique)
client_aliasstringrequiredClient organization name/alias
client_typestringrequiredType of client organization (CORPORATE, STARTUP, etc.)
country_codestringrequiredCountry code for PSP assignment and eKYC (2-letter ISO code)
phonestringrequiredContact phone number with country code
client_statusstringClient status (default: 'PENDING')
Request Body
{
"email": "admin@company.com",
"client_alias": "Tech Company Ltd",
"client_type": "CORPORATE",
"country_code": "ID",
"phone": "+628123456789",
"client_status": "PENDING"
}Response
{
"message": "Client & Admin successfully created! Admin must complete KYC before being able to use the account.",
"client": {
"client_id": 1,
"client_code": "CL-202401-0001",
"email": "admin@company.com",
"client_alias": "Tech Company Ltd",
"client_type": "CORPORATE",
"client_status": "PENDING",
"ekyc_status": "PENDING",
"country_code": "ID",
"psp_id": 11,
"phone": "+628123456789"
},
"admin_user": {
"user_id": 123,
"client_id": 1,
"username": "Tech Company Ltd",
"email": "admin@company.com",
"phone": "+628123456789",
"role": "admin",
"user_status": "ACTIVE",
"ekyc_status": "PENDING"
},
"ekyc_status": "PENDING",
"xendit_integration": {
"psp_provider": "XENDIT",
"business_customer": {
"status": "success",
"customer_id": "cust_business_1234567890",
"customer_type": "BUSINESS"
},
"admin_customer": {
"status": "success",
"customer_id": "cust_individual_0987654321",
"customer_type": "INDIVIDUAL"
}
}
}{
"error": "Email, Name, Type, Country and Mobile Number are required!"
}{
"error": "Email is already in use by another client."
}{
"error": "Failed to create Client",
"details": "Database transaction failed"
}{
"message": "Client & Admin successfully created! Admin must complete KYC before being able to use the account.",
"client": {
"client_id": 2,
"client_code": "CL-202401-0002",
"email": "admin@sgcompany.com",
"client_alias": "Singapore Tech Ltd",
"client_type": "CORPORATE",
"client_status": "PENDING",
"ekyc_status": "PENDING",
"country_code": "SG",
"psp_id": 2,
"phone": "+6591234567"
},
"admin_user": {
"user_id": 124,
"client_id": 2,
"username": "Singapore Tech Ltd",
"email": "admin@sgcompany.com",
"phone": "+6591234567",
"role": "admin",
"user_status": "ACTIVE",
"ekyc_status": "PENDING"
},
"ekyc_status": "PENDING"
}{
"error": "No PSP available for the selected country."
}{
"error": "There is no FIAT_PSP PSP available for that country."
}curl -X POST https://api.brdz.link/api/clients/create_with_admin \
-H "Content-Type: application/json" \
-d '{
"email": "admin@company.com",
"client_alias": "Tech Company Ltd",
"client_type": "CORPORATE",
"country_code": "ID",
"phone": "+628123456789"
}'Get All Clients
Get All Clients
Retrieve list of all clients in the system. Admin access required. Returns clients ordered by creation date (newest first) with PSP and contact information.
Response
[
{
"client_id": 1,
"client_code": "CL-202401-0001",
"email": "admin@company.com",
"client_alias": "Tech Company Ltd",
"client_type": "CORPORATE",
"client_status": "ACTIVE",
"ekyc_status": "APPROVED",
"country_code": "ID",
"psp_id": 11,
"phone": "+628123456789",
"created": "2024-01-15T10:30:00Z",
"updated": "2024-01-15T11:45:00Z"
},
{
"client_id": 2,
"client_code": "CL-202401-0002",
"email": "admin@startup.com",
"client_alias": "Startup Inc",
"client_type": "STARTUP",
"client_status": "PENDING",
"ekyc_status": "PENDING",
"country_code": "SG",
"psp_id": 2,
"phone": "+6591234567",
"created": "2024-01-14T09:15:00Z",
"updated": "2024-01-14T09:15:00Z"
},
{
"client_id": 3,
"client_code": "CL-202401-0003",
"email": "admin@usacorp.com",
"client_alias": "USA Corporation",
"client_type": "CORPORATE",
"client_status": "ACTIVE",
"ekyc_status": "APPROVED",
"country_code": "US",
"psp_id": 4,
"phone": "+15551234567",
"created": "2024-01-13T14:20:00Z",
"updated": "2024-01-13T16:30:00Z"
}
]{
"error": "Failed to fetch clients",
"details": "Database connection failed"
}curl -X GET https://api.brdz.link/api/clients \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"Get My Instances
Get My Instances
Retrieve instances belonging to the logged-in admin's client. Returns instance list with client information joined from database including eKYC and client code information.
Response
{
"instances": [
{
"instance_id": 1,
"client_id": 1,
"instance_name": "Production API",
"client_alias": "Prod Environment",
"instance_status": "ACTIVE",
"ekyc_status": "APPROVED",
"client_code": "CL-202401-0001",
"country_code": "ID"
},
{
"instance_id": 2,
"client_id": 1,
"instance_name": "Staging API",
"client_alias": "Test Environment",
"instance_status": "ACTIVE",
"ekyc_status": "APPROVED",
"client_code": "CL-202401-0001",
"country_code": "ID"
},
{
"instance_id": 3,
"client_id": 1,
"instance_name": "Development API",
"client_alias": null,
"instance_status": "ACTIVE",
"ekyc_status": "APPROVED",
"client_code": "CL-202401-0001",
"country_code": "ID"
}
]
}{
"error": "Client not found for this user."
}{
"error": "Failed to fetch instance:",
"details": "Database query failed"
}curl -X GET https://api.brdz.link/api/clients/my-instances \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"Create Instance
Create Instance
Create a new instance for the logged-in admin's client. Instance belongs to the client associated with the authenticated admin user. Requires admin authentication and valid client association.
Parameters
instance_namestringrequiredName for the new instance (unique within client)
Request Body
{
"instance_name": "Development API"
}Response
{
"message": "Instance created successfully",
"instance": {
"instance_id": 4,
"client_id": 1,
"instance_name": "Development API",
"client_alias": null,
"instance_status": "ACTIVE",
"created_at": "2024-01-15T12:00:00Z",
"updated_at": "2024-01-15T12:00:00Z"
}
}{
"error": "Instance name is required"
}{
"error": "Client ID not found"
}{
"error": "Internal server error"
}curl -X POST https://api.brdz.link/api/clients/create-instance \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY" \
-d '{"instance_name": "Development API"}'Update Instance Alias
Update Instance Alias
Update the display alias for an instance. Only the admin of the client that owns the instance can perform this update. Alias is optional display name for better identification.
Parameters
instance_idnumberrequiredInstance ID to update (path parameter)
aliasstringrequiredNew alias/display name for the instance
Request Body
{
"alias": "Production Environment v2"
}Response
{
"message": "✅ The alias was updated successfully"
}{
"error": "Alias and Instance ID are required."
}{
"error": "Access denied. The instance is not yours."
}{
"error": "Client not found"
}{
"error": "Failed to update alias",
"details": "Database update failed"
}curl -X PUT https://api.brdz.link/api/clients/1/update-alias \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY" \
-d '{"alias": "Production Environment v2"}'Delete Instance
Delete Instance
Delete an instance by ID. Only the admin of the client that owns the instance can perform this deletion. Permanent deletion from database - cannot be undone.
Parameters
instance_idnumberrequiredInstance ID to delete (path parameter)
Response
{
"message": "Instance deleted successfully"
}{
"error": "User not found or not linked to any client"
}{
"error": "Instance not found or does not belong to your client"
}{
"error": "Failed to delete instance",
"details": "Database deletion failed"
}curl -X DELETE https://api.brdz.link/api/clients/clients/1 \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"Client Creation Process
Automated Client Code Generation
Client codes follow the format: CL-YYYYMM-XXXX
const generateClientCode = async () => {
const yearMonth = new Date().toISOString().slice(0, 7).replace('-', '');
const prefix = `CL-${yearMonth}-`;
const result = await db.query(
`SELECT MAX(SUBSTRING(client_code FROM 11 FOR 4))::INTEGER AS max_seq
FROM clients
WHERE client_code LIKE $1`,
[`${prefix}%`]
);
const nextSeq = (result.rows[0]?.max_seq || 0) + 1;
const paddedSeq = String(nextSeq).padStart(4, '0');
return `${prefix}${paddedSeq}`;
};
Examples:
- January 2024:
CL-202401-0001,CL-202401-0002, etc. - February 2024:
CL-202402-0001,CL-202402-0002, etc.
Automated Features
- PSP Assignment: Automatic PSP selection based on country_code with fallback
- Admin User Creation: Secure password generation and user account setup
- Email Notification: Welcome email with login credentials sent automatically
- eKYC Initialization: Sets up PENDING eKYC status for compliance workflow
- Xendit Integration: Auto-create Xendit customers for Indonesia clients
- Email Logging: Track email delivery status in database
Database Transactions
All client creation operations use database transactions for data consistency:
try {
await db.query('BEGIN');
// 1. Email uniqueness validation
// 2. PSP selection and validation
// 3. Client code generation
// 4. Client record creation
// 5. Xendit business customer creation (Indonesia)
// 6. Admin user creation
// 7. Xendit individual customer creation (Indonesia admin)
// 8. Email logging
// 9. Email sending
await db.query('COMMIT');
} catch (error) {
await db.query('ROLLBACK');
throw error;
}
PSP Integration
PSP Selection Logic
// Primary PSP selection by country
const selectedPsp = await pspService.getPspByCountry(country_code);
// Fallback PSP query if primary fails
if (!selectedPsp?.psp_id) {
const fallbackPsp = await db.query(`
SELECT psp_id FROM psp_providers
WHERE country_code = $1 AND psp_type = 'FIAT_PSP'
ORDER BY priority ASC, created_at ASC
LIMIT 1
`, [country_code]);
psp_id = fallbackPsp.rows[0].psp_id;
}
PSP Types and Assignment
- FIAT_PSP: Fiat currency payment service providers
- Priority-based: Selection by configured priority levels
- Country-specific: PSPs assigned per country code
- Fallback System: Automatic fallback to available PSPs
Current PSP Providers:
- Indonesia (ID): XENDIT (PSP ID: 11)
- Singapore (SG): PSP_Singapore (PSP ID: 2)
- United States (US): PSP_USA (PSP ID: 4)
- India (IN): PSP_India (PSP ID: 3)
- Australia (AU): PSP_Australia (PSP ID: 6)
- Vietnam (VN): PSP_Vietnam (PSP ID: 10)
Xendit Integration
Auto-Creation for Indonesia Clients
When country_code = 'ID' and PSP is XENDIT, the system automatically creates:
- Business Customer (for the client organization)
- Individual Customer (for the admin user)
// Business customer creation
const clientPspData = {
client_id: newClient.client_id,
email: email,
client_alias: client_alias,
client_type: client_type,
phone: phone
};
const xenditCustomerResult = await pspService.getPspUser(clientPspData, country_code);
// Admin individual customer creation
const adminPspData = {
user_id: userResult.user_id,
email: email,
username: adminUser.username,
phone: phone
};
const adminXenditResult = await pspService.getPspUser(adminPspData, country_code);
Xendit Customer Types
-
BUSINESS: For client organizations
- Links to
client_id - Used for business operations and transactions
- Higher transaction limits
- Links to
-
INDIVIDUAL: For admin users
- Links to
user_id - Personal account for admin user
- Standard individual limits
- Links to
Xendit Error Handling
Xendit integration failures are non-blocking:
try {
xenditCustomerResult = await pspService.getPspUser(clientPspData, country_code);
} catch (xenditError) {
console.error('❌ [XENDIT] Error creating business customer:', xenditError.message);
// Don't fail the entire client creation, just log the error
console.warn(`⚠️ [XENDIT] Continuing client creation without Xendit integration`);
}
Email Service Integration
Admin Account Email Template
The system sends a welcome email with:
- Admin username and email
- Generated secure password
- Frontend login URL
- Account activation instructions
await sendEmail({
to: email,
subject: "Your AOL Core Admin Account",
htmlContent: sendEmailAdmin(adminUser.username, email, rawPassword, frontendUrl)
});
Secure Password Generation
const generateSecurePassword = () => {
const length = 10;
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@!#$%^&*()_+";
return Array.from(crypto.randomFillSync(new Uint8Array(length)))
.map(byte => charset[byte % charset.length])
.join('');
};
Email Logging
All email delivery is tracked in database:
INSERT INTO client_email_logs (client_id, email, subject, status, created_at)
VALUES ($1, $2, 'Your AOL Core Admin Account', 'SENT', CURRENT_TIMESTAMP)
Instance Management
Instance Lifecycle
- Creation: Admin creates instance with unique name within client
- Alias Management: Optional display name/alias updates for better identification
- Status Tracking: Active/inactive status management
- Deletion: Permanent removal from system (cannot be undone)
Access Control and Security
- Client Ownership: Instances belong to specific clients
- Admin Permissions: Only client admins can manage their instances
- Database Validation: Ownership checks ensure proper access control
- Transaction Safety: All operations use proper error handling
Instance Status Management
// Check user's client_id
const userResult = await db.query(`
SELECT client_id FROM users WHERE user_id = $1
`, [user_id]);
// Validate instance ownership before operations
const instanceResult = await db.query(`
SELECT * FROM instances WHERE instance_id = $1 AND client_id = $2
`, [instance_id, client_id]);
Database Schema
clients Table
CREATE TABLE clients (
client_id SERIAL PRIMARY KEY,
client_code VARCHAR(20) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
client_alias VARCHAR(255) NOT NULL,
client_type VARCHAR(50) NOT NULL,
client_status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
ekyc_status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
country_code VARCHAR(3) NOT NULL,
psp_id INTEGER REFERENCES psp_providers(psp_id),
phone VARCHAR(20) NOT NULL,
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
instances Table
CREATE TABLE instances (
instance_id SERIAL PRIMARY KEY,
client_id INTEGER NOT NULL REFERENCES clients(client_id) ON DELETE CASCADE,
instance_name VARCHAR(255) NOT NULL,
client_alias VARCHAR(255),
instance_status VARCHAR(20) DEFAULT 'ACTIVE',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
client_email_logs Table
CREATE TABLE client_email_logs (
id SERIAL PRIMARY KEY,
client_id INTEGER NOT NULL REFERENCES clients(client_id) ON DELETE CASCADE,
email VARCHAR(255) NOT NULL,
subject VARCHAR(255) NOT NULL,
status VARCHAR(20) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
xendit_customers Table
CREATE TABLE xendit_customers (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(user_id) ON DELETE CASCADE,
client_id INTEGER REFERENCES clients(client_id) ON DELETE CASCADE,
xendit_customer_id VARCHAR(255) UNIQUE NOT NULL,
customer_type customer_type_enum NOT NULL,
status VARCHAR(50) DEFAULT 'ACTIVE',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
xendit_accounts Table
CREATE TABLE xendit_accounts (
id SERIAL PRIMARY KEY,
xendit_customer_id VARCHAR(255) REFERENCES xendit_customers(xendit_customer_id),
account_type VARCHAR(50),
account_id VARCHAR(255),
currency VARCHAR(10) DEFAULT 'IDR',
balance NUMERIC(18,2) DEFAULT 0,
status VARCHAR(50) DEFAULT 'ACTIVE',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
psp_providers Table Reference
CREATE TABLE psp_providers (
psp_id SERIAL PRIMARY KEY,
psp_name VARCHAR(50) NOT NULL,
country_code VARCHAR(3) NOT NULL,
api_endpoint VARCHAR(255),
currency_code VARCHAR(10),
psp_type VARCHAR(20),
priority INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Security Features
Password Security
- Crypto-random Generation: 10-character passwords with mixed charset including special characters
- bcrypt Hashing: Secure password storage with salt rounds
- Email Delivery: Credentials sent via secure email service with logging
Access Control
- Role-based Access: Admin-only endpoints protected by authMiddleware and roleMiddleware
- Client Isolation: Users can only access their own client's instances
- Database Validation: Ownership checks before all operations
- JWT Authentication: Bearer token validation for all protected endpoints
Data Validation
- Required Fields: Email, alias, type, country, phone validation
- Email Uniqueness: Prevents duplicate client emails across system
- PSP Availability: Ensures valid PSP for country before client creation
- Transaction Safety: Database rollback on any failure
Xendit Security
- PSP Service Integration: Secure Xendit customer creation via pspService
- Country Validation: Xendit integration only for Indonesia (ID) clients
- Error Handling: Xendit failures don't prevent client creation
- Status Tracking: Customer status tracked in local database
Error Handling
Common Errors and Solutions
| HTTP | Error | Cause | Solution |
|---|---|---|---|
| 400 | Required fields missing | Missing email, alias, type, country, or phone | Provide all mandatory fields |
| 400 | No PSP available | Country has no configured PSP | Configure PSP for country or use different country |
| 400 | No FIAT PSP available | Country has PSP but not FIAT type | Configure FIAT_PSP for country |
| 409 | Email already in use | Duplicate email address | Use different email address |
| 403 | Client ID not found | User not linked to client | Ensure user has valid client association |
| 403 | Access denied | Instance not owned by user's client | Verify instance ownership |
| 404 | Client not found | Invalid client_id or user association | Check user-client relationship |
| 404 | Instance not found | Invalid instance_id or access denied | Verify instance exists and ownership |
| 500 | Database transaction failed | Server-side database issues | Retry request or contact support |
Error Handling Pattern
try {
const result = await brdzSDK.client.createClientWithAdmin(clientData);
// Handle success
} catch (error) {
if (error.message.includes('Email, Name, Type, Country and Mobile Number are required')) {
// Handle validation error
showFieldValidationErrors();
} else if (error.message.includes('Email is already in use')) {
// Handle duplicate email
showEmailConflictError();
} else if (error.message.includes('No PSP available')) {
// Handle PSP configuration issue
showPspConfigurationError();
} else if (error.message.includes('Access denied')) {
// Handle authorization error
showAccessDeniedError();
} else {
// Handle generic error
showGenericError(error.message);
}
}
SDK Usage Examples
Complete Client Management Flow
const brdzSDK = require('@anantla/brdz-sdk');
class ClientManager {
constructor(sdk) {
this.sdk = sdk;
}
async createNewClient(clientData) {
try {
const client = await this.sdk.client;
const result = await client.createClientWithAdmin(clientData);
console.log('Client created:', result.client.client_code);
console.log('Admin user:', result.admin_user.username);
// Check Xendit integration for Indonesia
if (result.xendit_integration) {
console.log('Xendit Business Customer:', result.xendit_integration.business_customer?.customer_id);
console.log('Xendit Admin Customer:', result.xendit_integration.admin_customer?.customer_id);
}
return result;
} catch (error) {
throw new Error(`Client creation failed: ${error.message}`);
}
}
async getClientInstances() {
try {
const client = await this.sdk.client;
const instances = await client.getMyInstances();
console.log('Total instances:', instances.instances.length);
return instances;
} catch (error) {
throw new Error(`Failed to get instances: ${error.message}`);
}
}
async createNewInstance(instanceName) {
try {
const client = await this.sdk.client;
const result = await client.createInstance({ instance_name: instanceName });
console.log('Instance created:', result.instance.instance_name);
return result;
} catch (error) {
throw new Error(`Instance creation failed: ${error.message}`);
}
}
async updateInstanceAlias(instanceId, alias) {
try {
const client = await this.sdk.client;
const result = await client.updateInstanceAlias(instanceId, { alias });
console.log('Alias updated:', result.message);
return result;
} catch (error) {
throw new Error(`Alias update failed: ${error.message}`);
}
}
async deleteInstance(instanceId) {
try {
const client = await this.sdk.client;
const result = await client.deleteInstance(instanceId);
console.log('Instance deleted:', result.message);
return result;
} catch (error) {
throw new Error(`Instance deletion failed: ${error.message}`);
}
}
}
// Usage example
const clientManager = new ClientManager(brdzSDK);
// Create new client with admin
const newClientData = {
email: 'admin@techcorp.com',
client_alias: 'Tech Corporation',
client_type: 'CORPORATE',
country_code: 'ID',
phone: '+628123456789'
};
clientManager.createNewClient(newClientData)
.then(result => {
console.log('Client setup complete');
console.log('Client Code:', result.client.client_code);
if (result.xendit_integration) {
console.log('Xendit integration active for Indonesia client');
}
})
.catch(error => {
console.error('Client creation failed:', error.message);
});
// Manage instances
clientManager.getClientInstances()
.then(instances => {
console.log('Current instances:', instances.instances.length);
// Create new instance
return clientManager.createNewInstance('API Gateway');
})
.then(newInstance => {
console.log('New instance created:', newInstance.instance.instance_id);
// Update its alias
return clientManager.updateInstanceAlias(newInstance.instance.instance_id, 'Main API Gateway');
})
.then(updateResult => {
console.log('Instance alias updated');
})
.catch(error => {
console.error('Instance management failed:', error.message);
});
Client creation automatically handles PSP assignment, admin user setup, eKYC initialization, and Xendit integration for Indonesia clients.
Instance deletion is permanent and cannot be undone. Ensure proper confirmation before deletion.
Indonesia clients (country_code: 'ID') automatically get Xendit business and individual customer accounts created through PSP service integration.
The delete instance endpoint uses /api/clients/clients/{instance_id} due to the base route structure where /clients is the base path and /clients/:instance_id is the specific endpoint.