curl --request POST \
--url https://laso.finance/withdraw-card-balance \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"amount": 25,
"destination_address": "0x1234567890abcdef1234567890abcdef12345678"
}
'import requests
url = "https://laso.finance/withdraw-card-balance"
payload = {
"amount": 25,
"destination_address": "0x1234567890abcdef1234567890abcdef12345678"
}
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({amount: 25, destination_address: '0x1234567890abcdef1234567890abcdef12345678'})
};
fetch('https://laso.finance/withdraw-card-balance', 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://laso.finance/withdraw-card-balance",
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' => 25,
'destination_address' => '0x1234567890abcdef1234567890abcdef12345678'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"Content-Type: application/json"
],
]);
$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://laso.finance/withdraw-card-balance"
payload := strings.NewReader("{\n \"amount\": 25,\n \"destination_address\": \"0x1234567890abcdef1234567890abcdef12345678\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<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://laso.finance/withdraw-card-balance")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 25,\n \"destination_address\": \"0x1234567890abcdef1234567890abcdef12345678\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://laso.finance/withdraw-card-balance")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 25,\n \"destination_address\": \"0x1234567890abcdef1234567890abcdef12345678\"\n}"
response = http.request(request)
puts response.read_body{
"withdrawal_id": "<string>",
"status": "requested",
"amount": 123,
"destination_address": "<string>",
"network": "base",
"asset": "USDC",
"note": "<string>"
}{
"error": "The user can withdraw at most $18.50 right now (open requests count against the balance). Ask them to pick an amount within that.",
"code": "insufficient_card_balance",
"hint": "Read the current balance with GET /get-card-deposit-address and request that amount or less. Withdrawals the issuer has not paid yet still count against it."
}{
"error": "Missing or invalid Authorization header"
}{
"error": "Account is frozen",
"frozen_message": "Your account is frozen pending a compliance review. Contact support@laso.finance."
}Withdraw from a reloadable card balance
Move unspent money off the account holder’s reloadable card. The balance behind a reloadable card is held by the card issuer, and this asks the issuer to pay amount of it out as USDC on Base to the destination_address you supply. It is the reverse of GET /fund-card-balance, and free: the money is the holder’s own, and Laso moves nothing itself.
Base only. The issuer pays on Base and nowhere else, so the destination must be a Base wallet you control. A Laso managed agent wallet holds USDC on Solana and cannot receive this. The card deposit address from GET /get-card-deposit-address is not a valid destination either.
Not instant. The issuer processes payouts manually, usually within 1-3 business days, and emails the account holder when the payout is sent. The response carries the issuer’s withdrawal_id; there is no status route to poll. The request is recorded as a withdrawal event under card_events in GET /list-card-transactions, and the balance from GET /get-card-deposit-address drops once it is paid.
Amount: $2 to $10,000, and no more than the balance the issuer can release. Withdrawals the issuer has not paid yet still count against the balance, so a second request can only take what remains.
Confirm the amount and destination with the account holder before calling this. A linked card issuer account is required; the holder sets it up at https://laso.finance/agent/dashboard/verified/card.
Requires a Bearer token from /auth or /get-card.
curl --request POST \
--url https://laso.finance/withdraw-card-balance \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"amount": 25,
"destination_address": "0x1234567890abcdef1234567890abcdef12345678"
}
'import requests
url = "https://laso.finance/withdraw-card-balance"
payload = {
"amount": 25,
"destination_address": "0x1234567890abcdef1234567890abcdef12345678"
}
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({amount: 25, destination_address: '0x1234567890abcdef1234567890abcdef12345678'})
};
fetch('https://laso.finance/withdraw-card-balance', 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://laso.finance/withdraw-card-balance",
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' => 25,
'destination_address' => '0x1234567890abcdef1234567890abcdef12345678'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"Content-Type: application/json"
],
]);
$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://laso.finance/withdraw-card-balance"
payload := strings.NewReader("{\n \"amount\": 25,\n \"destination_address\": \"0x1234567890abcdef1234567890abcdef12345678\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<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://laso.finance/withdraw-card-balance")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 25,\n \"destination_address\": \"0x1234567890abcdef1234567890abcdef12345678\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://laso.finance/withdraw-card-balance")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 25,\n \"destination_address\": \"0x1234567890abcdef1234567890abcdef12345678\"\n}"
response = http.request(request)
puts response.read_body{
"withdrawal_id": "<string>",
"status": "requested",
"amount": 123,
"destination_address": "<string>",
"network": "base",
"asset": "USDC",
"note": "<string>"
}{
"error": "The user can withdraw at most $18.50 right now (open requests count against the balance). Ask them to pick an amount within that.",
"code": "insufficient_card_balance",
"hint": "Read the current balance with GET /get-card-deposit-address and request that amount or less. Withdrawals the issuer has not paid yet still count against it."
}{
"error": "Missing or invalid Authorization header"
}{
"error": "Account is frozen",
"frozen_message": "Your account is frozen pending a compliance review. Contact support@laso.finance."
}Authorizations
Firebase ID token from /auth or any paid route, sent as a Bearer token: Authorization: Bearer <id_token> (the Bearer prefix is required).
Body
Response
The issuer accepted the withdrawal request.
The card issuer's reference for this payout request. Quote it to support; it also appears on the matching withdrawal card event.
Always requested: the issuer has accepted the request and will pay it out manually.
"requested"
USD the issuer will pay out.
The Base address the USDC goes to.
"base"
"USDC"
Timing and where to see the request afterwards.
Was this page helpful?