Create Withdraw
Creates a new withdraw/transfer to a customer bank account. The withdraw will be processed asynchronously.
Endpoint
POST /api/v1/withdraws
Authentication
This endpoint requires HMAC Integration Authentication.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | string | Yes | Amount as decimal string (e.g., "1000.00" = 10.00 THB) |
currency | string | No | Currency code. Defaults to "THB" |
referenceId | string | Yes | Your unique reference ID (alphanumeric, underscores, hyphens, dots, 1-255 chars) |
destinationType | string | Yes | Destination type: "bank_account" or "wallet" |
customerBankAccountName | string | Yes | Customer's bank account name |
customerBankAccountNumber | string | Yes | Customer's bank account number |
customerBankCode | string | Yes | 3-digit Thai bank code (e.g., "004" for KBANK) |
description | string | No | Withdraw description (max 255 characters) |
metadata | object | No | Additional metadata (key-value pairs) |
idempotencyKey | string | No | Unique key to prevent duplicate withdraws (recommended) |
Integration Context
Your merchantId and integrationId are automatically provided from your integration credentials. You don't need to include them in the request.
Example Request
import crypto from 'crypto';
const API_KEY = process.env.PAYMENT_API_KEY!;
const API_SECRET = process.env.PAYMENT_API_SECRET!;
async function createWithdraw() {
const method = 'POST';
const path = '/api/v1/withdraws';
const body = JSON.stringify({
amount: '1000.00',
currency: 'THB',
referenceId: `payout-${Date.now()}`,
destinationType: 'bank_account',
customerBankAccountName: 'John Doe',
customerBankAccountNumber: '1234567890',
customerBankCode: '004',
description: 'Payout for order #12345',
metadata: {
order_id: '12345',
},
idempotencyKey: `payout-12345-${Date.now()}`,
});
const timestamp = Date.now().toString();
const message = `${timestamp}.${body}`;
const signature = crypto
.createHmac('sha256', API_SECRET)
.update(message)
.digest('hex');
const response = await fetch(`https://api.example.com${path}`, {
method,
headers: {
'X-API-Key': API_KEY,
'X-Timestamp': timestamp,
'X-Signature': `sha256=${signature}`,
'Content-Type': 'application/json',
},
body,
});
const result = await response.json();
if (result.success) {
console.log('Withdraw created:', result.data.id);
console.log('Status:', result.data.status);
return result.data;
} else {
console.error('Error:', result.error);
throw new Error(result.error.message);
}
}
createWithdraw();Response
Success Response (201 Created)
{
"success": true,
"data": {
"id": "660e8400-e29b-41d4-a716-446655440001",
"amount": "1000.00",
"currency": "THB",
"status": "pending",
"referenceId": "payout-12345",
"destinationType": "bank_account",
"bankAccountNumber": "1234****7890",
"bankAccountName": "John Doe",
"bankCode": "004",
"destinationId": null,
"description": "Payout for order #12345",
"metadata": {
"order_id": "12345"
},
"paidAt": null,
"canceledAt": null,
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z"
}
}Error Response (400 Bad Request)
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "amount must be a valid decimal number",
"metadata": {
"field": "amount"
}
}
}Withdraw Status
After creation, withdraws typically start in pending status and progress to processing as they're handled by the payment provider.
Idempotency
Use Idempotency Keys
Always include an idempotencyKey in your requests to prevent duplicate withdraws if you need to retry a request.
If you make the same request twice with the same idempotencyKey, you'll receive the same withdraw object. This is useful for handling network errors and retries.
If you don't provide an idempotencyKey, the system will auto-generate one, but you won't be able to retry safely.
Error Codes
| Code | Description |
|---|---|
VALIDATION_ERROR | Invalid request parameters |
INVALID_AMOUNT | Amount must be a valid decimal number |
INVALID_BANK_CODE | Bank code must be a valid 3-digit Thai bank code |
INSUFFICIENT_BALANCE | Merchant account has insufficient balance |
DUPLICATE_REFERENCE_ID | Reference ID already exists for this merchant |
INVALID_CREDENTIALS | Authentication failed |
Related Endpoints
- List Withdraws - List all withdraws
- Retrieve Withdraw - Get withdraw details
- Cancel Withdraw - Cancel a withdraw
See Also
- Withdraws Overview
- Webhooks - Receive withdraw status updates
- Error Handling