curl --request POST \
--url https://api.rach.finance/caas/v1/users/withdraw \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"amount": "0.500000",
"phone": "+2250700000002",
"account_name": "Ama Mensah",
"account_number": "1234567890123",
"bank_code": "GH0100",
"bank_name": "GCB Bank",
"chain": "polygon",
"destination_type": "MOBILE_MONEY",
"idempotency_key": "wdrl_ref_001",
"payout_currency": "XOF",
"payout_mobile": "+2250700000002",
"payout_network": "orange_money",
"save_destination": false,
"token": "USDC",
"use_saved_destination": false
}
'import requests
url = "https://api.rach.finance/caas/v1/users/withdraw"
payload = {
"amount": "0.500000",
"phone": "+2250700000002",
"account_name": "Ama Mensah",
"account_number": "1234567890123",
"bank_code": "GH0100",
"bank_name": "GCB Bank",
"chain": "polygon",
"destination_type": "MOBILE_MONEY",
"idempotency_key": "wdrl_ref_001",
"payout_currency": "XOF",
"payout_mobile": "+2250700000002",
"payout_network": "orange_money",
"save_destination": False,
"token": "USDC",
"use_saved_destination": False
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
amount: '0.500000',
phone: '+2250700000002',
account_name: 'Ama Mensah',
account_number: '1234567890123',
bank_code: 'GH0100',
bank_name: 'GCB Bank',
chain: 'polygon',
destination_type: 'MOBILE_MONEY',
idempotency_key: 'wdrl_ref_001',
payout_currency: 'XOF',
payout_mobile: '+2250700000002',
payout_network: 'orange_money',
save_destination: false,
token: 'USDC',
use_saved_destination: false
})
};
fetch('https://api.rach.finance/caas/v1/users/withdraw', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.rach.finance/caas/v1/users/withdraw",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount' => '0.500000',
'phone' => '+2250700000002',
'account_name' => 'Ama Mensah',
'account_number' => '1234567890123',
'bank_code' => 'GH0100',
'bank_name' => 'GCB Bank',
'chain' => 'polygon',
'destination_type' => 'MOBILE_MONEY',
'idempotency_key' => 'wdrl_ref_001',
'payout_currency' => 'XOF',
'payout_mobile' => '+2250700000002',
'payout_network' => 'orange_money',
'save_destination' => false,
'token' => 'USDC',
'use_saved_destination' => false
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.rach.finance/caas/v1/users/withdraw"
payload := strings.NewReader("{\n \"amount\": \"0.500000\",\n \"phone\": \"+2250700000002\",\n \"account_name\": \"Ama Mensah\",\n \"account_number\": \"1234567890123\",\n \"bank_code\": \"GH0100\",\n \"bank_name\": \"GCB Bank\",\n \"chain\": \"polygon\",\n \"destination_type\": \"MOBILE_MONEY\",\n \"idempotency_key\": \"wdrl_ref_001\",\n \"payout_currency\": \"XOF\",\n \"payout_mobile\": \"+2250700000002\",\n \"payout_network\": \"orange_money\",\n \"save_destination\": false,\n \"token\": \"USDC\",\n \"use_saved_destination\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.rach.finance/caas/v1/users/withdraw")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": \"0.500000\",\n \"phone\": \"+2250700000002\",\n \"account_name\": \"Ama Mensah\",\n \"account_number\": \"1234567890123\",\n \"bank_code\": \"GH0100\",\n \"bank_name\": \"GCB Bank\",\n \"chain\": \"polygon\",\n \"destination_type\": \"MOBILE_MONEY\",\n \"idempotency_key\": \"wdrl_ref_001\",\n \"payout_currency\": \"XOF\",\n \"payout_mobile\": \"+2250700000002\",\n \"payout_network\": \"orange_money\",\n \"save_destination\": false,\n \"token\": \"USDC\",\n \"use_saved_destination\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.rach.finance/caas/v1/users/withdraw")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": \"0.500000\",\n \"phone\": \"+2250700000002\",\n \"account_name\": \"Ama Mensah\",\n \"account_number\": \"1234567890123\",\n \"bank_code\": \"GH0100\",\n \"bank_name\": \"GCB Bank\",\n \"chain\": \"polygon\",\n \"destination_type\": \"MOBILE_MONEY\",\n \"idempotency_key\": \"wdrl_ref_001\",\n \"payout_currency\": \"XOF\",\n \"payout_mobile\": \"+2250700000002\",\n \"payout_network\": \"orange_money\",\n \"save_destination\": false,\n \"token\": \"USDC\",\n \"use_saved_destination\": false\n}"
response = http.request(request)
puts response.read_body{
"message": "<string>",
"status": "<string>",
"withdrawal_id": "<string>"
}{}{}{}{}Initiate Off-Ramp Withdrawal
Sweeps stablecoin from a customer wallet to the Rach treasury via an ERC-4337 UserOperation, then delivers local currency to the recipient. Payout goes to mobile money or a bank account: set destination_type, or set use_saved_destination to pay the recipient’s own saved account when you know their phone number but not their account details.
Status lifecycle: PENDING → SUBMITTING → SUBMITTED → CRYPTO_RECEIVED → PAYOUT_SUBMITTING → COMPLETED. CRYPTO_RECEIVED means the crypto leg is done and the cash has not landed yet — do not tell a customer they have been paid until COMPLETED, which requires recorded evidence that the payout actually reached them. REFUNDED is a terminal outcome where the payout could not be delivered and the stablecoin was returned to the customer wallet; it is distinct from FAILED, which means nothing ever left the wallet. Never retry on SUBMISSION_UNKNOWN or PAYOUT_UNKNOWN: the operation may already have happened, and Rach reconciles it automatically.
curl --request POST \
--url https://api.rach.finance/caas/v1/users/withdraw \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"amount": "0.500000",
"phone": "+2250700000002",
"account_name": "Ama Mensah",
"account_number": "1234567890123",
"bank_code": "GH0100",
"bank_name": "GCB Bank",
"chain": "polygon",
"destination_type": "MOBILE_MONEY",
"idempotency_key": "wdrl_ref_001",
"payout_currency": "XOF",
"payout_mobile": "+2250700000002",
"payout_network": "orange_money",
"save_destination": false,
"token": "USDC",
"use_saved_destination": false
}
'import requests
url = "https://api.rach.finance/caas/v1/users/withdraw"
payload = {
"amount": "0.500000",
"phone": "+2250700000002",
"account_name": "Ama Mensah",
"account_number": "1234567890123",
"bank_code": "GH0100",
"bank_name": "GCB Bank",
"chain": "polygon",
"destination_type": "MOBILE_MONEY",
"idempotency_key": "wdrl_ref_001",
"payout_currency": "XOF",
"payout_mobile": "+2250700000002",
"payout_network": "orange_money",
"save_destination": False,
"token": "USDC",
"use_saved_destination": False
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
amount: '0.500000',
phone: '+2250700000002',
account_name: 'Ama Mensah',
account_number: '1234567890123',
bank_code: 'GH0100',
bank_name: 'GCB Bank',
chain: 'polygon',
destination_type: 'MOBILE_MONEY',
idempotency_key: 'wdrl_ref_001',
payout_currency: 'XOF',
payout_mobile: '+2250700000002',
payout_network: 'orange_money',
save_destination: false,
token: 'USDC',
use_saved_destination: false
})
};
fetch('https://api.rach.finance/caas/v1/users/withdraw', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.rach.finance/caas/v1/users/withdraw",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount' => '0.500000',
'phone' => '+2250700000002',
'account_name' => 'Ama Mensah',
'account_number' => '1234567890123',
'bank_code' => 'GH0100',
'bank_name' => 'GCB Bank',
'chain' => 'polygon',
'destination_type' => 'MOBILE_MONEY',
'idempotency_key' => 'wdrl_ref_001',
'payout_currency' => 'XOF',
'payout_mobile' => '+2250700000002',
'payout_network' => 'orange_money',
'save_destination' => false,
'token' => 'USDC',
'use_saved_destination' => false
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.rach.finance/caas/v1/users/withdraw"
payload := strings.NewReader("{\n \"amount\": \"0.500000\",\n \"phone\": \"+2250700000002\",\n \"account_name\": \"Ama Mensah\",\n \"account_number\": \"1234567890123\",\n \"bank_code\": \"GH0100\",\n \"bank_name\": \"GCB Bank\",\n \"chain\": \"polygon\",\n \"destination_type\": \"MOBILE_MONEY\",\n \"idempotency_key\": \"wdrl_ref_001\",\n \"payout_currency\": \"XOF\",\n \"payout_mobile\": \"+2250700000002\",\n \"payout_network\": \"orange_money\",\n \"save_destination\": false,\n \"token\": \"USDC\",\n \"use_saved_destination\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.rach.finance/caas/v1/users/withdraw")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": \"0.500000\",\n \"phone\": \"+2250700000002\",\n \"account_name\": \"Ama Mensah\",\n \"account_number\": \"1234567890123\",\n \"bank_code\": \"GH0100\",\n \"bank_name\": \"GCB Bank\",\n \"chain\": \"polygon\",\n \"destination_type\": \"MOBILE_MONEY\",\n \"idempotency_key\": \"wdrl_ref_001\",\n \"payout_currency\": \"XOF\",\n \"payout_mobile\": \"+2250700000002\",\n \"payout_network\": \"orange_money\",\n \"save_destination\": false,\n \"token\": \"USDC\",\n \"use_saved_destination\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.rach.finance/caas/v1/users/withdraw")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": \"0.500000\",\n \"phone\": \"+2250700000002\",\n \"account_name\": \"Ama Mensah\",\n \"account_number\": \"1234567890123\",\n \"bank_code\": \"GH0100\",\n \"bank_name\": \"GCB Bank\",\n \"chain\": \"polygon\",\n \"destination_type\": \"MOBILE_MONEY\",\n \"idempotency_key\": \"wdrl_ref_001\",\n \"payout_currency\": \"XOF\",\n \"payout_mobile\": \"+2250700000002\",\n \"payout_network\": \"orange_money\",\n \"save_destination\": false,\n \"token\": \"USDC\",\n \"use_saved_destination\": false\n}"
response = http.request(request)
puts response.read_body{
"message": "<string>",
"status": "<string>",
"withdrawal_id": "<string>"
}{}{}{}{}Authorizations
Your Rach B2B API key. Use rach_sk_live_* for production (on-chain) or rach_sk_test_* for sandbox (no-chain simulation).
Body
Withdrawal request
"0.500000"
"+2250700000002"
"Ama Mensah"
"1234567890123"
Bank account. Send account_number in whatever format the destination market uses — IBAN for XOF, XAF and Europe, NUBAN for Nigeria, and so on. It is passed through to the payout provider unmodified.
"GH0100"
"GCB Bank"
Chain selects the EVM settlement network the funds are drawn from — see TransferRequest.Chain.
"polygon"
DestinationType selects the payout rail: MOBILE_MONEY (default) or BANK_ACCOUNT.
"MOBILE_MONEY"
"wdrl_ref_001"
defaults to XOF
"XOF"
Mobile money. Required when destination_type is MOBILE_MONEY, unless use_saved_destination is set.
"+2250700000002"
"orange_money"
SaveDestination stores the details supplied here against the recipient, so a later payout can use use_saved_destination instead of repeating them.
false
"USDC"
UseSavedDestination pays to the recipient's own saved default for this currency instead of details supplied here. Use this when you know the recipient's phone number but not their account — they supply their own details once, on their own channel, and later payouts resolve to them.
false

