Logo
API ReferenceWithdraws

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

ParameterTypeRequiredDescription
amountstringYesAmount as decimal string (e.g., "1000.00" = 10.00 THB)
currencystringNoCurrency code. Defaults to "THB"
referenceIdstringYesYour unique reference ID (alphanumeric, underscores, hyphens, dots, 1-255 chars)
destinationTypestringYesDestination type: "bank_account" or "wallet"
customerBankAccountNamestringYesCustomer's bank account name
customerBankAccountNumberstringYesCustomer's bank account number
customerBankCodestringYes3-digit Thai bank code (e.g., "004" for KBANK)
descriptionstringNoWithdraw description (max 255 characters)
metadataobjectNoAdditional metadata (key-value pairs)
idempotencyKeystringNoUnique 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

CodeDescription
VALIDATION_ERRORInvalid request parameters
INVALID_AMOUNTAmount must be a valid decimal number
INVALID_BANK_CODEBank code must be a valid 3-digit Thai bank code
INSUFFICIENT_BALANCEMerchant account has insufficient balance
DUPLICATE_REFERENCE_IDReference ID already exists for this merchant
INVALID_CREDENTIALSAuthentication failed

See Also

On this page