curl --request POST \
--url https://api.qbank.cl/platform/v1/payins/{payinID}/refunds \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"amount": "150.00",
"kind": "refund",
"reason": "customer request",
"idempotency_key": "refund-order-8842"
}
'import requests
url = "https://api.qbank.cl/platform/v1/payins/{payinID}/refunds"
payload = {
"amount": "150.00",
"kind": "refund",
"reason": "customer request",
"idempotency_key": "refund-order-8842"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
amount: '150.00',
kind: 'refund',
reason: 'customer request',
idempotency_key: 'refund-order-8842'
})
};
fetch('https://api.qbank.cl/platform/v1/payins/{payinID}/refunds', 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.qbank.cl/platform/v1/payins/{payinID}/refunds",
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' => '150.00',
'kind' => 'refund',
'reason' => 'customer request',
'idempotency_key' => 'refund-order-8842'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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://api.qbank.cl/platform/v1/payins/{payinID}/refunds"
payload := strings.NewReader("{\n \"amount\": \"150.00\",\n \"kind\": \"refund\",\n \"reason\": \"customer request\",\n \"idempotency_key\": \"refund-order-8842\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.qbank.cl/platform/v1/payins/{payinID}/refunds")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": \"150.00\",\n \"kind\": \"refund\",\n \"reason\": \"customer request\",\n \"idempotency_key\": \"refund-order-8842\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.qbank.cl/platform/v1/payins/{payinID}/refunds")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": \"150.00\",\n \"kind\": \"refund\",\n \"reason\": \"customer request\",\n \"idempotency_key\": \"refund-order-8842\"\n}"
response = http.request(request)
puts response.read_body{
"refund_id": "6f1a2b3c-4d5e-4f6a-8b9c-0d1e2f3a4b5c",
"payin_id": "d0135ed5-8e9c-4f8b-a522-8ec100470426",
"account_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"kind": "refund",
"status": "completed",
"currency": "USD",
"local_amount": "150.00",
"usdt_debited": "150.000000",
"requested_by": "account",
"idempotency_key": "refund-order-8842",
"reason": "customer request",
"idempotency_hit": true,
"receipt_url": "https://api.qbank.cl/platform/v1/payin-refunds/6f1a2b3c-4d5e-4f6a-8b9c-0d1e2f3a4b5c/receipt",
"created_at": "2026-07-25T13:10:00Z",
"updated_at": "2026-07-25T13:10:03Z"
}{
"refund_id": "6f1a2b3c-4d5e-4f6a-8b9c-0d1e2f3a4b5c",
"payin_id": "d0135ed5-8e9c-4f8b-a522-8ec100470426",
"account_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"kind": "refund",
"status": "completed",
"currency": "USD",
"local_amount": "150.00",
"usdt_debited": "150.000000",
"requested_by": "account",
"idempotency_key": "refund-order-8842",
"reason": "customer request",
"receipt_url": "https://api.qbank.cl/platform/v1/payin-refunds/6f1a2b3c-4d5e-4f6a-8b9c-0d1e2f3a4b5c/receipt",
"created_at": "2026-07-25T13:10:00Z",
"updated_at": "2026-07-25T13:10:03Z"
}{
"refund_id": "6f1a2b3c-4d5e-4f6a-8b9c-0d1e2f3a4b5c",
"payin_id": "d0135ed5-8e9c-4f8b-a522-8ec100470426",
"account_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"kind": "refund",
"status": "pending",
"currency": "USD",
"local_amount": "150.00",
"usdt_debited": "150.000000",
"requested_by": "account",
"idempotency_key": "refund-order-8842",
"reconciliation_required": true,
"receipt_url": "https://api.qbank.cl/platform/v1/payin-refunds/6f1a2b3c-4d5e-4f6a-8b9c-0d1e2f3a4b5c/receipt",
"created_at": "2026-07-25T13:10:00Z",
"updated_at": "2026-07-25T13:10:00Z"
}{
"error": "idempotency_key_required",
"message": "idempotency_key is required"
}{
"error": "unauthorized",
"message": "invalid or missing credentials"
}{
"error": "insufficient_funds",
"message": "the account balance is not enough to fund this refund; top up and retry with the same idempotency_key"
}{
"error": "forbidden",
"message": "credentials required"
}{
"error": "not_found",
"message": "resource not found"
}{
"error": "idempotency_conflict",
"message": "another refund with this key is in flight"
}{
"error": "refund_exceeds_payin",
"message": "the requested amount exceeds what is left to refund on this payin"
}Refund a card payin
Returns money to the cardholder and reverses the credit on your balance. Only card-acquired payins in status credited can be refunded (refund_not_supported on the other rails) — and only once their balance is actually available: a credited payin whose settlement is still pending (settlement_pending: true, balance lands at settle_at) is declined with 422 settlement_pending. The gross amount is debited: the fee and the FX spread of the original payin are NOT refundable. idempotency_key is mandatory; a replay returns the same refund with idempotency_hit and never sends a second refund to the processor. Requires OTP when the caller is a session (API keys are exempt).
curl --request POST \
--url https://api.qbank.cl/platform/v1/payins/{payinID}/refunds \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"amount": "150.00",
"kind": "refund",
"reason": "customer request",
"idempotency_key": "refund-order-8842"
}
'import requests
url = "https://api.qbank.cl/platform/v1/payins/{payinID}/refunds"
payload = {
"amount": "150.00",
"kind": "refund",
"reason": "customer request",
"idempotency_key": "refund-order-8842"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
amount: '150.00',
kind: 'refund',
reason: 'customer request',
idempotency_key: 'refund-order-8842'
})
};
fetch('https://api.qbank.cl/platform/v1/payins/{payinID}/refunds', 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.qbank.cl/platform/v1/payins/{payinID}/refunds",
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' => '150.00',
'kind' => 'refund',
'reason' => 'customer request',
'idempotency_key' => 'refund-order-8842'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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://api.qbank.cl/platform/v1/payins/{payinID}/refunds"
payload := strings.NewReader("{\n \"amount\": \"150.00\",\n \"kind\": \"refund\",\n \"reason\": \"customer request\",\n \"idempotency_key\": \"refund-order-8842\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.qbank.cl/platform/v1/payins/{payinID}/refunds")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": \"150.00\",\n \"kind\": \"refund\",\n \"reason\": \"customer request\",\n \"idempotency_key\": \"refund-order-8842\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.qbank.cl/platform/v1/payins/{payinID}/refunds")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": \"150.00\",\n \"kind\": \"refund\",\n \"reason\": \"customer request\",\n \"idempotency_key\": \"refund-order-8842\"\n}"
response = http.request(request)
puts response.read_body{
"refund_id": "6f1a2b3c-4d5e-4f6a-8b9c-0d1e2f3a4b5c",
"payin_id": "d0135ed5-8e9c-4f8b-a522-8ec100470426",
"account_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"kind": "refund",
"status": "completed",
"currency": "USD",
"local_amount": "150.00",
"usdt_debited": "150.000000",
"requested_by": "account",
"idempotency_key": "refund-order-8842",
"reason": "customer request",
"idempotency_hit": true,
"receipt_url": "https://api.qbank.cl/platform/v1/payin-refunds/6f1a2b3c-4d5e-4f6a-8b9c-0d1e2f3a4b5c/receipt",
"created_at": "2026-07-25T13:10:00Z",
"updated_at": "2026-07-25T13:10:03Z"
}{
"refund_id": "6f1a2b3c-4d5e-4f6a-8b9c-0d1e2f3a4b5c",
"payin_id": "d0135ed5-8e9c-4f8b-a522-8ec100470426",
"account_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"kind": "refund",
"status": "completed",
"currency": "USD",
"local_amount": "150.00",
"usdt_debited": "150.000000",
"requested_by": "account",
"idempotency_key": "refund-order-8842",
"reason": "customer request",
"receipt_url": "https://api.qbank.cl/platform/v1/payin-refunds/6f1a2b3c-4d5e-4f6a-8b9c-0d1e2f3a4b5c/receipt",
"created_at": "2026-07-25T13:10:00Z",
"updated_at": "2026-07-25T13:10:03Z"
}{
"refund_id": "6f1a2b3c-4d5e-4f6a-8b9c-0d1e2f3a4b5c",
"payin_id": "d0135ed5-8e9c-4f8b-a522-8ec100470426",
"account_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"kind": "refund",
"status": "pending",
"currency": "USD",
"local_amount": "150.00",
"usdt_debited": "150.000000",
"requested_by": "account",
"idempotency_key": "refund-order-8842",
"reconciliation_required": true,
"receipt_url": "https://api.qbank.cl/platform/v1/payin-refunds/6f1a2b3c-4d5e-4f6a-8b9c-0d1e2f3a4b5c/receipt",
"created_at": "2026-07-25T13:10:00Z",
"updated_at": "2026-07-25T13:10:00Z"
}{
"error": "idempotency_key_required",
"message": "idempotency_key is required"
}{
"error": "unauthorized",
"message": "invalid or missing credentials"
}{
"error": "insufficient_funds",
"message": "the account balance is not enough to fund this refund; top up and retry with the same idempotency_key"
}{
"error": "forbidden",
"message": "credentials required"
}{
"error": "not_found",
"message": "resource not found"
}{
"error": "idempotency_conflict",
"message": "another refund with this key is in flight"
}{
"error": "refund_exceeds_payin",
"message": "the requested amount exceeds what is left to refund on this payin"
}Authorizations
Session JWT (from register/login) or API key (pk_...).
X-API-Key: <token> is accepted as an alternative header.
Headers
One-time-use OTP token (see the OTP guide). Only for session callers.
Path Parameters
Body
Response
Replay of the same idempotency key — the SAME refund, with idempotency_hit.
chargeback is imposed by the issuer — it is booked automatically and may leave the balance negative.
refund, void, chargeback pending, completed, failed Currency of the original payin.
Amount debited from the USDT balance. The fee and the FX spread of the original payin are NOT refunded.
account, admin, issuer Present when status is failed — the debit was reversed in full.
Present while the refund is pending — the outcome is resolved by reconciliation and is never re-dispatched.
Present in the payin_refunded webhook — the USDT balance after the movement (can be negative on a chargeback).