Create Refund
Issue a full or partial refund for a completed order. Refunds are processed back to the original payment method (HSA/FSA card or regular card).
Authentication
Authorization: Bearer glm_test_YOUR_API_KEY
Request Body
{
"order_id" : "ord_abc123xyz" ,
"amount_cents" : 4235 ,
"reason" : "customer_request" ,
"notes" : "Customer requested refund due to delayed shipping" ,
"metadata" : {
"support_ticket" : "TICKET-12345"
}
}
Parameters
Parameter Type Required Description order_idstring Yes ID of the order to refund amount_centsinteger No Amount to refund in cents (omit for full refund) reasonenum Yes Refund reason (see below) notesstring No Additional notes (max 500 chars) metadataobject No Custom key-value pairs
Finding Order ID : You can get the order_id from:
Webhook event data (order.created event)
Order API response (GET /v2/orders)
Your database (store it when receiving order.created webhook)
Dashboard order details
Refund Reasons
Reason Description Example Use Case customer_requestCustomer requested refund Customer changed mind, no longer needed duplicateDuplicate payment Customer accidentally paid twice fraudulentFraudulent order Detected fraudulent transaction product_unavailableProduct out of stock Item went out of stock after order damaged_productProduct arrived damaged Item damaged during shipping wrong_productWrong product shipped Fulfillment error, sent wrong item otherOther reason Any other reason (explain in notes)
Request
Response
{
"id" : "ref_xyz789" ,
"order_id" : "ord_abc123xyz" ,
"amount_cents" : 4235 ,
"reason" : "customer_request" ,
"notes" : "Customer requested refund due to delayed shipping" ,
"status" : "pending" ,
"order" : {
"id" : "ord_abc123xyz" ,
"order_number" : "ORD-2025-001234" ,
"payment_status" : "refunded"
},
"refund_breakdown" : {
"hsa_fsa_amount_cents" : 2995 ,
"regular_amount_cents" : 1240
},
"metadata" : {
"support_ticket" : "TICKET-12345"
},
"created_at" : "2025-10-18T16:00:00Z" ,
"processed_at" : null ,
"estimated_arrival" : "2025-10-25T16:00:00Z"
}
Response Fields
Field Type Description idstring Unique refund identifier statusenum Refund status: pending, succeeded, failed, cancelled refund_breakdownobject Split of refund by payment type processed_attimestamp When refund was processed (null if pending) estimated_arrivaltimestamp Estimated date funds return to customer
Examples
Full Refund
curl -X POST https://api.withgale.com/v2/refunds \
-H "Authorization: Bearer glm_test_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "ord_abc123xyz",
"reason": "customer_request",
"notes": "Customer not satisfied with product"
}'
Partial Refund
curl -X POST https://api.withgale.com/v2/refunds \
-H "Authorization: Bearer glm_test_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "ord_abc123xyz",
"amount_cents": 1000,
"reason": "damaged_product",
"notes": "Partial refund for damaged item"
}'
With JavaScript
const createRefund = async ( orderId , amount , reason , notes ) => {
const response = await fetch ( 'https://api.withgale.com/v2/refunds' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ process . env . GALE_API_KEY } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
order_id: orderId ,
amount_cents: amount ,
reason: reason ,
notes: notes
})
});
if ( ! response . ok ) {
throw new Error ( 'Failed to create refund' );
}
const refund = await response . json ();
console . log ( `Refund ${ refund . id } created` );
return refund ;
};
// Full refund
await createRefund (
'ord_abc123xyz' ,
null , // null = full refund
'customer_request' ,
'Customer changed mind'
);
// Partial refund
await createRefund (
'ord_abc123xyz' ,
1500 , // $15.00
'damaged_product' ,
'One item damaged in shipping'
);
Refund Status
Status Description pendingRefund initiated, processing succeededRefund processed successfully failedRefund failed cancelledRefund cancelled
Common Use Cases
Customer Service Refund
const refund = await createRefund ({
order_id: orderId ,
amount_cents: amount ,
reason: 'customer_request' ,
notes: `Support ticket: ${ ticketId } ` ,
metadata: {
support_ticket: ticketId ,
agent: req . user . id
}
});
Cancelled Orders
// Only refund if payment was captured
if ( order . payment_status === 'captured' ) {
await createRefund ({
order_id: orderId ,
reason: 'other' ,
notes: `Order cancelled: ${ reason } `
});
}
Partial Refund for Specific Items
// Calculate refund amount for damaged items
const refundAmount = order . line_items
. filter ( item => damagedItemIds . includes ( item . id ))
. reduce (( sum , item ) => sum + item . price_cents * item . quantity , 0 );
await createRefund ({
order_id: orderId ,
amount_cents: refundAmount ,
reason: 'damaged_product' ,
notes: `Refunding items: ${ damagedItemIds . join ( ', ' ) } `
});
Split Card Refunds (Automatic)
Gale automatically handles split card refunds. You don’t need to specify how much goes to each card.
When an order is paid with both HSA/FSA and regular card:
{
"order" : {
"amounts" : {
"hsa_fsa_amount_cents" : 4995 , // Paid with HSA/FSA card
"regular_amount_cents" : 895 , // Paid with regular card
"total_cents" : 5890
}
}
}
Full Refund
Just provide the order_id - Gale splits automatically:
POST /v2/refunds
{
"order_id" : "ord_abc123",
"reason" : "customer_request"
}
Response shows the breakdown:
{
"id" : "ref_xyz789" ,
"amount_cents" : 5890 ,
"refund_breakdown" : {
"hsa_fsa_amount_cents" : 4995 , // Refunded to HSA/FSA card
"regular_amount_cents" : 895 // Refunded to regular card
}
}
Partial Refund
Gale splits proportionally based on the original payment ratio:
POST /v2/refunds
{
"order_id" : "ord_abc123",
"amount_cents" : 2945, // Partial refund
"reason" : "damaged_product"
}
Response:
{
"id" : "ref_xyz789" ,
"amount_cents" : 2945 ,
"refund_breakdown" : {
"hsa_fsa_amount_cents" : 2498 , // ~84.7% (same ratio as original)
"regular_amount_cents" : 447 // ~15.3%
}
}
Key Points:
You only specify total amount_cents
Gale calculates the split automatically
Each amount refunds to its original payment method
Response shows the breakdown for your records
Webhooks
Refunds trigger the following webhook events:
{
"type" : "refund.created" ,
"data" : {
"id" : "ref_xyz789" ,
"order_id" : "ord_abc123xyz" ,
"amount_cents" : 4235 ,
"status" : "pending"
}
}
{
"type" : "refund.succeeded" ,
"data" : {
"id" : "ref_xyz789" ,
"order_id" : "ord_abc123xyz" ,
"status" : "succeeded" ,
"processed_at" : "2025-10-18T16:05:00Z"
}
}
See Webhooks Reference for all refund events.
Errors
Status Code Error Code Description 400 invalid_amountRefund amount exceeds order total 400 already_refundedOrder already fully refunded 400 invalid_stateOrder not in refundable state 401 unauthorizedInvalid or missing API key 404 not_foundOrder not found 422 validation_errorField validation failed
Amount Too Large
{
"error" : {
"code" : "invalid_amount" ,
"message" : "Refund amount exceeds order total" ,
"details" : {
"requested" : 10000 ,
"maximum" : 4235
}
}
}
Already Refunded
{
"error" : {
"code" : "already_refunded" ,
"message" : "Order has already been fully refunded"
}
}
Invalid State
{
"error" : {
"code" : "invalid_state" ,
"message" : "Cannot refund order with payment status 'pending'"
}
}
Refund Timeline
Payment Method Typical Arrival Time HSA/FSA Card 5-10 business days Regular Card 5-10 business days
Note: Actual timing depends on the customer’s card issuer.
Best Practices
Verify Order Status Check payment_status === 'captured' before refunding
Provide Clear Reasons Always include reason and notes for audit trail
Notify Customers Send email notification when refund is processed
Track in Your System Store refund IDs and status in your database
Important Notes
Refunds cannot be undone. Once a refund is processed, it cannot be reversed. Double-check order ID and amount before submitting.
Partial refunds are supported. You can issue multiple partial refunds up to the order total. The remaining refundable amount is tracked automatically.