Skip to main content

Recipients - Bank Account Management

Recipients represent bank accounts that you want to transfer funds to. This guide covers creating, managing, and validating recipients for secure and efficient payouts.

Overviewโ€‹

Recipients in Omise allow you to:

  • Store Bank Details: Securely save bank account information
  • Validate Accounts: Verify bank account details before transfers
  • Manage Multiple Recipients: Handle multiple payout destinations
  • Automate Transfers: Use recipients in scheduled transfers
  • Track History: See all transfers made to each recipient
  • Update Information: Modify recipient details as needed

Key Featuresโ€‹

  • Bank Account Validation: Verify account numbers and bank codes
  • Multiple Bank Support: Support for various Thai and international banks
  • Metadata: Add custom information to recipients
  • Status Tracking: Monitor recipient verification status
  • Secure Storage: Bank details are securely encrypted
  • Easy Updates: Modify recipient information anytime

Creating Recipientsโ€‹

Basic Recipient Creationโ€‹

const omise = require('omise')({
secretKey: 'skey_test_123456789',
});

// Create a recipient for Thai bank account
async function createRecipient() {
try {
const recipient = await omise.recipients.create({
name: 'John Doe',
email: 'john.doe@example.com',
type: 'individual',
bank_account: {
brand: 'bbl',
number: '1234567890',
name: 'John Doe'
},
description: 'Supplier payment account',
metadata: {
supplier_id: 'SUP-001',
category: 'raw_materials'
}
});

console.log('Recipient created:', recipient.id);
console.log('Bank:', recipient.bank_account.brand);
console.log('Status:', recipient.active ? 'Active' : 'Inactive');

return recipient;
} catch (error) {
console.error('Failed to create recipient:', error.message);
throw error;
}
}

// Create recipient with validation
async function createValidatedRecipient(recipientData) {
// Validate required fields
if (!recipientData.name || !recipientData.bank_account) {
throw new Error('Name and bank account are required');
}

// Validate bank account number format
const accountNumber = recipientData.bank_account.number;
if (!/^\d{10,12}$/.test(accountNumber)) {
throw new Error('Invalid bank account number format');
}

try {
const recipient = await omise.recipients.create({
name: recipientData.name,
email: recipientData.email,
type: recipientData.type || 'individual',
bank_account: {
brand: recipientData.bank_account.brand,
number: recipientData.bank_account.number,
name: recipientData.bank_account.name
},
description: recipientData.description,
metadata: recipientData.metadata
});

console.log(`โœ“ Recipient created successfully: ${recipient.id}`);
return recipient;

} catch (error) {
console.error('โœ— Recipient creation failed:', error.message);
throw error;
}
}

// Example usage
createRecipient();

createValidatedRecipient({
name: 'Jane Smith',
email: 'jane@example.com',
type: 'individual',
bank_account: {
brand: 'scb',
number: '9876543210',
name: 'Jane Smith'
},
description: 'Freelancer payment',
metadata: {
contractor_id: 'CONT-042',
department: 'marketing'
}
});

API Responseโ€‹

{
"object": "recipient",
"id": "recp_test_5xyz789abc",
"livemode": false,
"location": "/recipients/recp_test_5xyz789abc",
"verified": true,
"active": true,
"name": "John Doe",
"email": "john.doe@example.com",
"description": "Supplier payment account",
"type": "individual",
"tax_id": null,
"bank_account": {
"object": "bank_account",
"brand": "bbl",
"last_digits": "7890",
"name": "John Doe",
"created": "2024-01-15T10:30:00Z"
},
"failure_code": null,
"created": "2024-01-15T10:30:00Z",
"metadata": {
"supplier_id": "SUP-001",
"category": "raw_materials"
}
}

Retrieving Recipientsโ€‹

Get Single Recipientโ€‹

async function getRecipient(recipientId) {
const recipient = await omise.recipients.retrieve(recipientId);

console.log('Recipient:', recipient.name);
console.log('Bank:', recipient.bank_account.brand);
console.log('Account ending:', recipient.bank_account.last_digits);
console.log('Verified:', recipient.verified);
console.log('Active:', recipient.active);

return recipient;
}

List All Recipientsโ€‹

def list_recipients(limit=100):
"""List all recipients with pagination"""
recipients = omise.Recipient.retrieve()

print(f"Total recipients: {recipients.total}")

for recipient in recipients.data:
print(f"\nID: {recipient.id}")
print(f"Name: {recipient.name}")
print(f"Bank: {recipient.bank_account.brand}")
print(f"Status: {'Active' if recipient.active else 'Inactive'}")

return recipients

def search_recipients(query):
"""Search recipients by name or email"""
all_recipients = omise.Recipient.retrieve()

query_lower = query.lower()
matches = [
r for r in all_recipients.data
if query_lower in r.name.lower() or
(r.email and query_lower in r.email.lower())
]

return matches

Updating Recipientsโ€‹

Update Recipient Informationโ€‹

def update_recipient(recipient_id, updates)
begin
recipient = Omise::Recipient.retrieve(recipient_id)
recipient.update(updates)

puts "Recipient updated: #{recipient.id}"
puts "New name: #{recipient.name}" if updates[:name]
puts "New email: #{recipient.email}" if updates[:email]

recipient
rescue Omise::Error => e
puts "Failed to update recipient: #{e.message}"
raise
end
end

# Update email
update_recipient('recp_test_123', email: 'newemail@example.com')

# Update description and metadata
update_recipient('recp_test_123', {
description: 'Updated description',
metadata: {
updated_at: Time.now.iso8601,
updated_by: 'admin'
}
})

Recipient Verificationโ€‹

Verification Statusโ€‹

function checkRecipientVerification($recipientId) {
$recipient = OmiseRecipient::retrieve($recipientId);

$status = [
'id' => $recipient['id'],
'verified' => $recipient['verified'],
'active' => $recipient['active'],
'failure_code' => $recipient['failure_code']
];

if (!$recipient['verified']) {
echo "โš  Recipient not verified\n";
if ($recipient['failure_code']) {
echo "Reason: {$recipient['failure_code']}\n";
}
} else {
echo "โœ“ Recipient verified and active\n";
}

return $status;
}

Common Use Casesโ€‹

1. Supplier Management Systemโ€‹

class SupplierRecipientManager {
constructor() {
this.suppliers = new Map();
}

async addSupplier(supplierData) {
// Create recipient
const recipient = await omise.recipients.create({
name: supplierData.companyName,
email: supplierData.email,
type: 'corporation',
bank_account: {
brand: supplierData.bankCode,
number: supplierData.accountNumber,
name: supplierData.accountName
},
description: `Supplier: ${supplierData.companyName}`,
metadata: {
supplier_id: supplierData.supplierId,
tax_id: supplierData.taxId,
payment_terms: supplierData.paymentTerms,
category: supplierData.category
}
});

// Store mapping
this.suppliers.set(supplierData.supplierId, recipient.id);

console.log(`Supplier ${supplierData.companyName} added as recipient ${recipient.id}`);
return recipient;
}

async getSupplierRecipient(supplierId) {
const recipientId = this.suppliers.get(supplierId);
if (!recipientId) {
throw new Error(`No recipient found for supplier ${supplierId}`);
}
return await omise.recipients.retrieve(recipientId);
}

async updateSupplierBankDetails(supplierId, newBankDetails) {
// Note: Bank account details can't be updated, need to create new recipient
const oldRecipient = await this.getSupplierRecipient(supplierId);

// Deactivate old recipient
await omise.recipients.update(oldRecipient.id, { active: false });

// Create new recipient
const newRecipient = await omise.recipients.create({
name: oldRecipient.name,
email: oldRecipient.email,
type: oldRecipient.type,
bank_account: newBankDetails,
description: oldRecipient.description,
metadata: {
...oldRecipient.metadata,
previous_recipient: oldRecipient.id,
updated_at: new Date().toISOString()
}
});

// Update mapping
this.suppliers.set(supplierId, newRecipient.id);

return newRecipient;
}
}

2. Employee Payout Systemโ€‹

class EmployeePayoutManager:
def __init__(self):
self.employee_recipients = {}

def add_employee(self, employee_data):
"""Add employee as recipient for payroll"""
recipient = omise.Recipient.create(
name=employee_data['full_name'],
email=employee_data['email'],
type='individual',
bank_account={
'brand': employee_data['bank_code'],
'number': employee_data['account_number'],
'name': employee_data['account_name']
},
description=f"Employee: {employee_data['employee_id']}",
metadata={
'employee_id': employee_data['employee_id'],
'department': employee_data['department'],
'position': employee_data['position'],
'hire_date': employee_data['hire_date']
}
)

self.employee_recipients[employee_data['employee_id']] = recipient.id
print(f"Employee {employee_data['full_name']} added for payouts")

return recipient

def verify_all_employees(self):
"""Verify all employee recipients"""
unverified = []

for emp_id, recipient_id in self.employee_recipients.items():
recipient = omise.Recipient.retrieve(recipient_id)

if not recipient.verified:
unverified.append({
'employee_id': emp_id,
'recipient_id': recipient_id,
'name': recipient.name,
'failure_code': recipient.failure_code
})

if unverified:
print(f"\nโš  {len(unverified)} employees have unverified bank accounts:")
for emp in unverified:
print(f" - {emp['name']} (ID: {emp['employee_id']})")
if emp['failure_code']:
print(f" Reason: {emp['failure_code']}")
else:
print("โœ“ All employee bank accounts verified")

return unverified

def batch_add_employees(self, employees_list):
"""Batch add multiple employees"""
results = {
'successful': [],
'failed': []
}

for employee in employees_list:
try:
recipient = self.add_employee(employee)
results['successful'].append({
'employee_id': employee['employee_id'],
'recipient_id': recipient.id
})
except Exception as e:
results['failed'].append({
'employee_id': employee['employee_id'],
'error': str(e)
})

print(f"\nBatch processing complete:")
print(f"Success: {len(results['successful'])}")
print(f"Failed: {len(results['failed'])}")

return results

3. Marketplace Vendor Payoutsโ€‹

class VendorPayoutManager
def initialize
@vendors = {}
end

def onboard_vendor(vendor_data)
# Validate vendor information
validate_vendor(vendor_data)

# Create recipient
recipient = Omise::Recipient.create(
name: vendor_data[:business_name],
email: vendor_data[:email],
type: vendor_data[:business_type], # 'individual' or 'corporation'
bank_account: {
brand: vendor_data[:bank_code],
number: vendor_data[:account_number],
name: vendor_data[:account_name]
},
description: "Vendor: #{vendor_data[:vendor_id]}",
tax_id: vendor_data[:tax_id],
metadata: {
vendor_id: vendor_data[:vendor_id],
commission_rate: vendor_data[:commission_rate],
category: vendor_data[:category],
onboarded_at: Time.now.iso8601
}
)

@vendors[vendor_data[:vendor_id]] = recipient.id

puts "โœ“ Vendor #{vendor_data[:business_name]} onboarded"
puts " Recipient ID: #{recipient.id}"
puts " Commission: #{vendor_data[:commission_rate]}%"

recipient
end

def validate_vendor(vendor_data)
required_fields = [:business_name, :email, :vendor_id, :bank_code, :account_number]
missing = required_fields.select { |f| vendor_data[f].nil? || vendor_data[f].empty? }

unless missing.empty?
raise ArgumentError, "Missing required fields: #{missing.join(', ')}"
end

# Validate bank account
validator = BankAccountValidator.new
validation = validator.validate(vendor_data[:bank_code], vendor_data[:account_number])

unless validation[:valid]
raise ArgumentError, "Invalid bank account: #{validation[:errors].join(', ')}"
end
end

def get_vendor_recipient(vendor_id)
recipient_id = @vendors[vendor_id]
raise "Vendor #{vendor_id} not found" unless recipient_id

Omise::Recipient.retrieve(recipient_id)
end

def list_pending_verifications
pending = []

@vendors.each do |vendor_id, recipient_id|
recipient = Omise::Recipient.retrieve(recipient_id)

unless recipient.verified
pending << {
vendor_id: vendor_id,
recipient_id: recipient_id,
business_name: recipient.name,
failure_code: recipient.failure_code
}
end
end

pending
end
end

Best Practicesโ€‹

1. Validate Before Creationโ€‹

async function validateAndCreateRecipient(data) {
const errors = [];

// Validate name
if (!data.name || data.name.length < 2) {
errors.push('Name must be at least 2 characters');
}

// Validate email format
if (data.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
errors.push('Invalid email format');
}

// Validate bank account
if (!data.bank_account || !data.bank_account.brand || !data.bank_account.number) {
errors.push('Bank account details are required');
}

if (errors.length > 0) {
throw new Error(`Validation failed:\n${errors.join('\n')}`);
}

return await omise.recipients.create(data);
}

2. Handle Verification Failuresโ€‹

def create_recipient_with_retry(recipient_data, max_retries=3):
"""Create recipient with automatic retry on verification failure"""

for attempt in range(max_retries):
try:
recipient = omise.Recipient.create(**recipient_data)

# Check verification status
if not recipient.verified:
print(f"โš  Recipient created but not verified: {recipient.failure_code}")

if recipient.failure_code == 'invalid_account_number':
# Prompt user to verify account number
print("Please verify the account number and try again")
return None
elif attempt < max_retries - 1:
# Retry after delay
time.sleep(2 ** attempt)
continue

return recipient

except omise.errors.BaseError as e:
if attempt < max_retries - 1:
print(f"Attempt {attempt + 1} failed, retrying...")
time.sleep(2 ** attempt)
else:
raise

return None

3. Maintain Audit Trailโ€‹

class RecipientAuditLog
def self.log_creation(recipient, context = {})
log_entry = {
event: 'recipient_created',
recipient_id: recipient.id,
name: recipient.name,
bank: recipient.bank_account.brand,
verified: recipient.verified,
created_by: context[:user_id],
timestamp: Time.now.iso8601,
metadata: recipient.metadata
}

# Save to audit log
save_audit_log(log_entry)
end

def self.log_update(recipient_id, changes, context = {})
log_entry = {
event: 'recipient_updated',
recipient_id: recipient_id,
changes: changes,
updated_by: context[:user_id],
timestamp: Time.now.iso8601
}

save_audit_log(log_entry)
end

def self.save_audit_log(entry)
File.open('recipient_audit.log', 'a') do |f|
f.puts JSON.generate(entry)
end
end
end

FAQโ€‹

Can I update bank account details for a recipient?โ€‹

No, bank account details cannot be updated once a recipient is created. If bank account information changes, you need to create a new recipient and optionally deactivate the old one.

What happens if recipient verification fails?โ€‹

If verification fails, the recipient will have verified: false and a failure_code indicating the issue. Common codes include invalid_account_number and bank_account_not_found. You'll need to correct the information and create a new recipient.

How long does recipient verification take?โ€‹

Recipient verification is typically instant when created. The system validates the bank account format and checks against bank databases in real-time.

Can I have multiple recipients with the same bank account?โ€‹

Yes, you can create multiple recipients with the same bank account details. This might be useful for tracking different payment purposes or departments.

What's the difference between 'individual' and 'corporation' type?โ€‹

The type field indicates whether the recipient is an individual person or a corporation. This affects how the recipient appears in your records and may be required for tax and compliance purposes.

How do I find a recipient by bank account number?โ€‹

Recipients store only the last 4 digits of the account number for security. You'll need to track the mapping between your internal IDs and recipient IDs in your own database.

Can I delete a recipient?โ€‹

Recipients cannot be deleted for audit trail purposes, but you can set active: false to deactivate them. Deactivated recipients cannot receive new transfers.

Do I need to create recipients for all transfers?โ€‹

Yes, you must create a recipient before making a transfer. Recipients represent the bank accounts you want to send money to.

Next Stepsโ€‹