Automated Transfer Schedules
Transfer schedules allow you to automate recurring transfers to recipients. This guide covers creating, managing, and monitoring automated transfer schedules.
Overviewโ
Transfer schedules enable:
- Automated Payouts: Set up recurring transfers without manual intervention
- Flexible Frequency: Daily, weekly, or monthly schedules
- Multiple Recipients: Schedule transfers to multiple recipients
- Balance Management: Automatic handling of insufficient balance scenarios
- Status Tracking: Monitor scheduled transfers and their execution
- Easy Management: Update or pause schedules as needed
Key Featuresโ
- Recurring Transfers: Automate regular payouts
- Multiple Schedules: Create schedules for different recipients
- Flexible Timing: Choose frequency and start dates
- Automatic Retry: Retry failed transfers automatically
- Notifications: Webhook events for schedule executions
- Pause/Resume: Temporarily stop schedules without deleting
Creating Transfer Schedulesโ
Basic Schedule Creationโ
- Node.js
- Python
- Ruby
- PHP
- Go
const omise = require('omise')({
secretKey: 'skey_test_123456789',
});
// Create a weekly transfer schedule
async function createWeeklySchedule(recipientId, amount) {
try {
const schedule = await omise.schedules.create({
every: 1,
period: 'week',
on: {
weekday_of_month: 'second_monday' // Every second Monday
},
start_date: '2024-02-01',
end_date: '2024-12-31',
charge: {
recipient: recipientId,
amount: amount,
metadata: {
schedule_type: 'weekly_payout',
purpose: 'contractor_payment'
}
}
});
console.log('Schedule created:', schedule.id);
console.log('Status:', schedule.status);
console.log('Next occurrence:', schedule.next_occurrence_dates[0]);
return schedule;
} catch (error) {
console.error('Failed to create schedule:', error.message);
throw error;
}
}
// Create monthly salary schedule
async function createMonthlySalarySchedule(recipientId, amount) {
const schedule = await omise.schedules.create({
every: 1,
period: 'month',
on: {
day_of_month: 25 // 25th of every month
},
start_date: '2024-01-01',
charge: {
recipient: recipientId,
amount: amount,
metadata: {
schedule_type: 'monthly_salary',
payment_type: 'payroll'
}
}
});
return schedule;
}
// Create daily schedule
async function createDailySchedule(recipientId, amount) {
const schedule = await omise.schedules.create({
every: 1,
period: 'day',
start_date: '2024-01-01',
charge: {
recipient: recipientId,
amount: amount,
metadata: {
schedule_type: 'daily_settlement'
}
}
});
return schedule;
}
// Example usage
createWeeklySchedule('recp_test_123456789', 500000);
createMonthlySalarySchedule('recp_test_123456789', 5000000);
import omise
from datetime import date
omise.api_secret = 'skey_test_123456789'
def create_weekly_schedule(recipient_id, amount):
"""Create a weekly transfer schedule"""
try:
schedule = omise.Schedule.create(
every=1,
period='week',
on={
'weekday_of_month': 'second_monday'
},
start_date='2024-02-01',
end_date='2024-12-31',
charge={
'recipient': recipient_id,
'amount': amount,
'metadata': {
'schedule_type': 'weekly_payout',
'purpose': 'contractor_payment'
}
}
)
print(f"Schedule created: {schedule.id}")
print(f"Status: {schedule.status}")
print(f"Next occurrence: {schedule.next_occurrence_dates[0]}")
return schedule
except omise.errors.BaseError as e:
print(f"Failed to create schedule: {str(e)}")
raise
def create_monthly_salary_schedule(recipient_id, amount):
"""Create monthly salary payment schedule"""
schedule = omise.Schedule.create(
every=1,
period='month',
on={'day_of_month': 25},
start_date='2024-01-01',
charge={
'recipient': recipient_id,
'amount': amount,
'metadata': {
'schedule_type': 'monthly_salary',
'payment_type': 'payroll'
}
}
)
return schedule
def create_biweekly_schedule(recipient_id, amount):
"""Create bi-weekly schedule (every 2 weeks)"""
schedule = omise.Schedule.create(
every=2,
period='week',
on={'weekday': 'friday'},
start_date='2024-01-05',
charge={
'recipient': recipient_id,
'amount': amount,
'metadata': {
'schedule_type': 'biweekly_payout'
}
}
)
return schedule
# Example usage
create_weekly_schedule('recp_test_123456789', 500000)
create_monthly_salary_schedule('recp_test_123456789', 5000000)
require 'omise'
Omise.api_key = 'skey_test_123456789'
# Create a weekly schedule
def create_weekly_schedule(recipient_id, amount)
begin
schedule = Omise::Schedule.create(
every: 1,
period: 'week',
on: {
weekday_of_month: 'second_monday'
},
start_date: '2024-02-01',
end_date: '2024-12-31',
charge: {
recipient: recipient_id,
amount: amount,
metadata: {
schedule_type: 'weekly_payout',
purpose: 'contractor_payment'
}
}
)
puts "Schedule created: #{schedule.id}"
puts "Status: #{schedule.status}"
puts "Next occurrence: #{schedule.next_occurrence_dates[0]}"
schedule
rescue Omise::Error => e
puts "Failed to create schedule: #{e.message}"
raise
end
end
# Create monthly salary schedule
def create_monthly_salary_schedule(recipient_id, amount)
Omise::Schedule.create(
every: 1,
period: 'month',
on: { day_of_month: 25 },
start_date: '2024-01-01',
charge: {
recipient: recipient_id,
amount: amount,
metadata: {
schedule_type: 'monthly_salary',
payment_type: 'payroll'
}
}
)
end
# Create quarterly schedule
def create_quarterly_schedule(recipient_id, amount)
Omise::Schedule.create(
every: 3,
period: 'month',
on: { day_of_month: 1 },
start_date: '2024-01-01',
charge: {
recipient: recipient_id,
amount: amount,
metadata: {
schedule_type: 'quarterly_payout'
}
}
)
end
# Example usage
create_weekly_schedule('recp_test_123456789', 500000)
create_monthly_salary_schedule('recp_test_123456789', 5000000)
<?php
require_once 'vendor/autoload.php';
define('OMISE_SECRET_KEY', 'skey_test_123456789');
// Create a weekly schedule
function createWeeklySchedule($recipientId, $amount) {
try {
$schedule = OmiseSchedule::create([
'every' => 1,
'period' => 'week',
'on' => [
'weekday_of_month' => 'second_monday'
],
'start_date' => '2024-02-01',
'end_date' => '2024-12-31',
'charge' => [
'recipient' => $recipientId,
'amount' => $amount,
'metadata' => [
'schedule_type' => 'weekly_payout',
'purpose' => 'contractor_payment'
]
]
]);
echo "Schedule created: {$schedule['id']}\n";
echo "Status: {$schedule['status']}\n";
echo "Next occurrence: {$schedule['next_occurrence_dates'][0]}\n";
return $schedule;
} catch (Exception $e) {
echo "Failed to create schedule: {$e->getMessage()}\n";
throw $e;
}
}
// Create monthly salary schedule
function createMonthlySalarySchedule($recipientId, $amount) {
$schedule = OmiseSchedule::create([
'every' => 1,
'period' => 'month',
'on' => ['day_of_month' => 25],
'start_date' => '2024-01-01',
'charge' => [
'recipient' => $recipientId,
'amount' => $amount,
'metadata' => [
'schedule_type' => 'monthly_salary',
'payment_type' => 'payroll'
]
]
]);
return $schedule;
}
// Create daily schedule
function createDailySchedule($recipientId, $amount) {
$schedule = OmiseSchedule::create([
'every' => 1,
'period' => 'day',
'start_date' => date('Y-m-d'),
'charge' => [
'recipient' => $recipientId,
'amount' => $amount,
'metadata' => [
'schedule_type' => 'daily_settlement'
]
]
]);
return $schedule;
}
// Example usage
createWeeklySchedule('recp_test_123456789', 500000);
createMonthlySalarySchedule('recp_test_123456789', 5000000);
?>
package main
import (
"fmt"
"log"
"github.com/omise/omise-go"
"github.com/omise/omise-go/operations"
)
const secretKey = "skey_test_123456789"
// CreateWeeklySchedule creates a weekly transfer schedule
func CreateWeeklySchedule(recipientID string, amount int64) (*omise.Schedule, error) {
client, err := omise.NewClient(secretKey, "")
if err != nil {
return nil, fmt.Errorf("failed to create client: %w", err)
}
schedule := &omise.Schedule{}
err = client.Do(schedule, &operations.CreateSchedule{
Every: 1,
Period: omise.SchedulePeriodWeek,
On: &omise.ScheduleOn{
WeekdayOfMonth: "second_monday",
},
StartDate: "2024-02-01",
EndDate: "2024-12-31",
Transfer: &omise.ScheduleTransferParams{
Recipient: recipientID,
Amount: amount,
Metadata: map[string]interface{}{
"schedule_type": "weekly_payout",
"purpose": "contractor_payment",
},
},
})
if err != nil {
return nil, fmt.Errorf("failed to create schedule: %w", err)
}
fmt.Printf("Schedule created: %s\n", schedule.ID)
fmt.Printf("Status: %s\n", schedule.Status)
return schedule, nil
}
// CreateMonthlySalarySchedule creates a monthly salary payment schedule
func CreateMonthlySalarySchedule(recipientID string, amount int64) (*omise.Schedule, error) {
client, err := omise.NewClient(secretKey, "")
if err != nil {
return nil, err
}
schedule := &omise.Schedule{}
err = client.Do(schedule, &operations.CreateSchedule{
Every: 1,
Period: omise.SchedulePeriodMonth,
On: &omise.ScheduleOn{
DayOfMonth: 25,
},
StartDate: "2024-01-01",
Transfer: &omise.ScheduleTransferParams{
Recipient: recipientID,
Amount: amount,
Metadata: map[string]interface{}{
"schedule_type": "monthly_salary",
"payment_type": "payroll",
},
},
})
return schedule, err
}
func main() {
schedule, err := CreateWeeklySchedule("recp_test_123456789", 500000)
if err != nil {
log.Fatalf("Failed to create schedule: %v", err)
}
fmt.Printf("Created schedule: %s\n", schedule.ID)
}
Managing Schedulesโ
List All Schedulesโ
async function listSchedules() {
const schedules = await omise.schedules.list();
for (const schedule of schedules.data) {
console.log(`${schedule.id}: ${schedule.status}`);
console.log(` Period: Every ${schedule.every} ${schedule.period}(s)`);
console.log(` Next: ${schedule.next_occurrence_dates[0]}`);
}
return schedules;
}
Update Scheduleโ
def update_schedule(schedule_id, updates):
"""Update schedule details"""
schedule = omise.Schedule.retrieve(schedule_id)
schedule.update(**updates)
print(f"Schedule {schedule_id} updated")
return schedule
# Pause schedule
update_schedule('schd_test_123', {'status': 'suspended'})
# Resume schedule
update_schedule('schd_test_123', {'status': 'active'})
# Update end date
update_schedule('schd_test_123', {'end_date': '2025-12-31'})
Delete Scheduleโ
def delete_schedule(schedule_id)
schedule = Omise::Schedule.retrieve(schedule_id)
schedule.destroy
puts "Schedule #{schedule_id} deleted"
end
Common Use Casesโ
1. Payroll Automationโ
class PayrollAutomation {
async setupEmployeeSchedules(employees) {
const schedules = [];
for (const employee of employees) {
const schedule = await omise.schedules.create({
every: 1,
period: 'month',
on: { day_of_month: 25 },
start_date: employee.start_date,
charge: {
recipient: employee.recipient_id,
amount: employee.monthly_salary,
metadata: {
employee_id: employee.id,
employee_name: employee.name,
department: employee.department,
schedule_type: 'monthly_payroll'
}
}
});
schedules.push({
employee_id: employee.id,
schedule_id: schedule.id
});
console.log(`โ Payroll schedule created for ${employee.name}`);
}
return schedules;
}
async updateSalary(employeeId, newSalary) {
// Find schedule
const scheduleId = this.getScheduleForEmployee(employeeId);
// Update schedule amount
const schedule = await omise.schedules.retrieve(scheduleId);
await schedule.update({
charge: {
...schedule.charge,
amount: newSalary
}
});
console.log(`Salary updated for employee ${employeeId}`);
}
}
2. Subscription Vendor Paymentsโ
class SubscriptionPayments:
def __init__(self):
self.schedules = {}
def setup_vendor_schedule(self, vendor_id, recipient_id, amount, period):
"""Set up recurring vendor payments"""
schedule = omise.Schedule.create(
every=1,
period=period, # 'month', 'week', etc.
start_date=date.today().isoformat(),
charge={
'recipient': recipient_id,
'amount': amount,
'metadata': {
'vendor_id': vendor_id,
'payment_type': 'subscription',
'period': period
}
}
)
self.schedules[vendor_id] = schedule.id
print(f"โ Schedule created for vendor {vendor_id}")
return schedule
def pause_vendor_payments(self, vendor_id):
"""Temporarily pause vendor payments"""
schedule_id = self.schedules.get(vendor_id)
if schedule_id:
schedule = omise.Schedule.retrieve(schedule_id)
schedule.update(status='suspended')
print(f"Payments paused for vendor {vendor_id}")
def resume_vendor_payments(self, vendor_id):
"""Resume vendor payments"""
schedule_id = self.schedules.get(vendor_id)
if schedule_id:
schedule = omise.Schedule.retrieve(schedule_id)
schedule.update(status='active')
print(f"Payments resumed for vendor {vendor_id}")
3. Marketplace Seller Payoutsโ
class MarketplacePayoutSchedules
def setup_seller_schedule(seller_id, recipient_id, payout_frequency)
# Determine schedule parameters
schedule_config = case payout_frequency
when 'daily'
{ every: 1, period: 'day' }
when 'weekly'
{ every: 1, period: 'week', on: { weekday: 'monday' } }
when 'monthly'
{ every: 1, period: 'month', on: { day_of_month: 1 } }
end
schedule = Omise::Schedule.create(
**schedule_config,
start_date: Date.today.to_s,
charge: {
recipient: recipient_id,
# Amount will be calculated based on sales
metadata: {
seller_id: seller_id,
payout_type: 'marketplace_earnings',
frequency: payout_frequency
}
}
)
puts "โ Payout schedule created for seller #{seller_id}"
puts " Frequency: #{payout_frequency}"
schedule
end
def adjust_payout_frequency(seller_id, new_frequency)
# Delete old schedule and create new one
old_schedule_id = get_schedule_for_seller(seller_id)
Omise::Schedule.retrieve(old_schedule_id).destroy
recipient_id = get_recipient_for_seller(seller_id)
setup_seller_schedule(seller_id, recipient_id, new_frequency)
end
end
Schedule Monitoringโ
Track Schedule Executionsโ
class ScheduleMonitor {
public function checkScheduleStatus($scheduleId) {
$schedule = OmiseSchedule::retrieve($scheduleId);
$status = [
'id' => $schedule['id'],
'status' => $schedule['status'],
'next_occurrences' => $schedule['next_occurrence_dates'],
'last_execution' => $this->getLastExecution($scheduleId)
];
if ($schedule['status'] === 'suspended') {
echo "โ Schedule is suspended\n";
} elseif ($schedule['status'] === 'active') {
echo "โ Schedule is active\n";
echo "Next run: {$schedule['next_occurrence_dates'][0]}\n";
}
return $status;
}
public function getFailedExecutions($scheduleId) {
// Get schedule occurrences
$schedule = OmiseSchedule::retrieve($scheduleId);
$occurrences = $schedule['occurrences']['data'];
$failed = array_filter($occurrences, function($occ) {
return $occ['status'] === 'failed';
});
return $failed;
}
private function getLastExecution($scheduleId) {
$schedule = OmiseSchedule::retrieve($scheduleId);
$occurrences = $schedule['occurrences']['data'];
return !empty($occurrences) ? $occurrences[0] : null;
}
}
Best Practicesโ
1. Handle Insufficient Balanceโ
// Set up webhook to handle schedule execution failures
app.post('/webhooks/omise', async (req, res) => {
const event = req.body;
if (event.key === 'schedule.occurrence.failed') {
const occurrence = event.data;
if (occurrence.failure_code === 'insufficient_balance') {
console.log(`Schedule ${occurrence.schedule} failed: insufficient balance`);
// Notify finance team
await notifyFinanceTeam({
schedule_id: occurrence.schedule,
required_amount: occurrence.charge.amount,
failure_reason: occurrence.failure_message
});
// Queue for retry when balance is available
await queueScheduleRetry(occurrence.schedule);
}
}
res.sendStatus(200);
});
2. Monitor Schedule Healthโ
def monitor_schedule_health():
"""Monitor all schedules for issues"""
schedules = omise.Schedule.list()
issues = []
for schedule in schedules.data:
if schedule.status == 'suspended':
issues.append({
'schedule_id': schedule.id,
'issue': 'suspended',
'action': 'Review and resume if needed'
})
# Check recent failures
occurrences = schedule.occurrences.data
recent_failures = [o for o in occurrences[:5] if o.status == 'failed']
if len(recent_failures) >= 3:
issues.append({
'schedule_id': schedule.id,
'issue': 'multiple_failures',
'failure_count': len(recent_failures),
'action': 'Investigate recipient or balance issues'
})
if issues:
print(f"โ Found {len(issues)} schedule issues:")
for issue in issues:
print(f" - {issue['schedule_id']}: {issue['issue']}")
return issues
3. Audit Trailโ
class ScheduleAuditLog
def self.log_schedule_event(event_type, schedule_id, details = {})
log_entry = {
event: event_type,
schedule_id: schedule_id,
timestamp: Time.now.iso8601,
details: details
}
File.open('schedule_audit.log', 'a') do |f|
f.puts JSON.generate(log_entry)
end
end
end
# Usage
ScheduleAuditLog.log_schedule_event('schedule_created', schedule.id, {
recipient: schedule.charge.recipient,
amount: schedule.charge.amount,
frequency: "#{schedule.every} #{schedule.period}"
})
FAQโ
Can I change the amount for scheduled transfers?โ
Yes, you can update the schedule to change the transfer amount. However, changes only affect future transfers, not past or pending ones.
What happens if I don't have sufficient balance?โ
If your balance is insufficient when a scheduled transfer is due, the transfer will fail with an insufficient_balance error. You can set up webhooks to be notified and handle this scenario.
Can I create schedules for multiple recipients?โ
Yes, you need to create a separate schedule for each recipient. Each schedule represents transfers to one recipient only.
How do I stop scheduled transfers temporarily?โ
Update the schedule status to suspended. This pauses the schedule without deleting it. You can resume by setting the status back to active.
Can I backdate a schedule?โ
No, you cannot create schedules with a start date in the past. The start date must be today or a future date.
What timezone are schedules executed in?โ
Schedules are executed in the timezone configured for your Omise account, typically Thailand timezone (UTC+7).
How far in advance can I see upcoming scheduled transfers?โ
The schedule object includes a next_occurrence_dates array showing upcoming execution dates. Typically shows the next 5-10 occurrences.
What happens when a schedule ends?โ
When a schedule reaches its end_date, it automatically becomes inactive and no longer executes. You can create a new schedule if you want to continue transfers.
Related Resourcesโ
- Recipients Management - Create and manage recipients
- Creating Transfers - Manual transfer creation
- Balance Management - Monitor account balance
- Webhooks - Handle schedule events
- API Reference - Complete schedule API
Next Stepsโ
- Set up webhook handlers for schedule events
- Monitor your account balance for scheduled transfers
- Review transaction history for executed transfers
- Implement reconciliation for accounting
- Learn about creating transfers manually