Create refund with restock_all_items accounting for existing partial refunds
Refunds all or part of an order through the payment provider that took the payment. type picks the portion: all, tax, shipping, product_price, cv, qv, or partial with an amount. Restock every remaining unrefunded item with restock_all_items, or name items in restock_items; items refunded earlier are not restocked twice. Set cancel_order to control whether a full refund of an unshipped order also cancels it. Requires a Bearer token for a company admin with the refunds update permission. Returns 404 when the order does not belong to the caller’s company, and 202 when the provider accepted the refund but has not settled it.
curl --request POST \
--url https://api.fluid.app/api/refunds \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"order_id": 48213,
"type": "partial",
"amount": 10,
"reason": "Two reams arrived water-damaged.",
"restock_items": [
{
"item_id": 130577,
"quantity": 1
}
],
"cancel_order": false
}
'import requests
url = "https://api.fluid.app/api/refunds"
payload = {
"order_id": 48213,
"type": "partial",
"amount": 10,
"reason": "Two reams arrived water-damaged.",
"restock_items": [
{
"item_id": 130577,
"quantity": 1
}
],
"cancel_order": False
}
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({
order_id: 48213,
type: 'partial',
amount: 10,
reason: 'Two reams arrived water-damaged.',
restock_items: [{item_id: 130577, quantity: 1}],
cancel_order: false
})
};
fetch('https://api.fluid.app/api/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.fluid.app/api/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([
'order_id' => 48213,
'type' => 'partial',
'amount' => 10,
'reason' => 'Two reams arrived water-damaged.',
'restock_items' => [
[
'item_id' => 130577,
'quantity' => 1
]
],
'cancel_order' => false
]),
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.fluid.app/api/refunds"
payload := strings.NewReader("{\n \"order_id\": 48213,\n \"type\": \"partial\",\n \"amount\": 10,\n \"reason\": \"Two reams arrived water-damaged.\",\n \"restock_items\": [\n {\n \"item_id\": 130577,\n \"quantity\": 1\n }\n ],\n \"cancel_order\": false\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.fluid.app/api/refunds")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"order_id\": 48213,\n \"type\": \"partial\",\n \"amount\": 10,\n \"reason\": \"Two reams arrived water-damaged.\",\n \"restock_items\": [\n {\n \"item_id\": 130577,\n \"quantity\": 1\n }\n ],\n \"cancel_order\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/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 \"order_id\": 48213,\n \"type\": \"partial\",\n \"amount\": 10,\n \"reason\": \"Two reams arrived water-damaged.\",\n \"restock_items\": [\n {\n \"item_id\": 130577,\n \"quantity\": 1\n }\n ],\n \"cancel_order\": false\n}"
response = http.request(request)
puts response.read_body{
"refund": {
"id": 20958,
"order_id": 48213,
"amount": 10,
"reason": "Two reams arrived water-damaged.",
"refund_type": "partial",
"order": {
"id": 48213,
"first_name": "Holly",
"last_name": "Flax",
"email": "holly.flax@dundermifflin.com",
"order_number": "DM-10482",
"status": "awaiting_shipment",
"source": "admin",
"amount": "63.41",
"currency_code": "USD",
"token": "fo_7Kq2ZtW9xYbN4cRhJ3mLpV",
"refundable_amount": "53.41",
"order_status": "completed",
"fulfillment_status": "unfulfilled",
"financial_status": "partially_refunded",
"warehouse_id": 311,
"created_at": "2026-09-14T15:40:52Z",
"updated_at": "2026-09-16T10:04:37Z"
}
},
"meta": {
"request_id": "6d2f8b41-3e7a-4c95-b1d0-9a5c7e2f4b83",
"timestamp": "2026-09-16T10:04:37Z"
}
}{
"message": "The payment provider accepted the refund and will confirm it once it settles.",
"refund_status": "pending",
"meta": {
"request_id": "1f9c4a72-8d3b-4e06-a5e1-c2b7d9f0e364",
"timestamp": "2026-09-16T10:04:37Z"
}
}{
"message": "<string>",
"error": "<string>",
"error_message": "<string>",
"errors": "<string>",
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"code": "<string>",
"blocked_date": "2026-11-30",
"blocked_day": 30,
"allowed_date": "2026-11-28",
"scheduled_date": "2026-12-10",
"last_bill_date": "2026-11-10",
"requested_date": "2026-11-30",
"payment_uuid": "<string>",
"error_details": [
{
"code": "<string>",
"field": "<string>",
"payment": "not_attempted",
"message": "<string>"
}
],
"coded_recovery": true
}
}{
"message": "<string>",
"error": "<string>",
"error_message": "<string>",
"errors": "<string>",
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"code": "<string>",
"blocked_date": "2026-11-30",
"blocked_day": 30,
"allowed_date": "2026-11-28",
"scheduled_date": "2026-12-10",
"last_bill_date": "2026-11-10",
"requested_date": "2026-11-30",
"payment_uuid": "<string>",
"error_details": [
{
"code": "<string>",
"field": "<string>",
"payment": "not_attempted",
"message": "<string>"
}
],
"coded_recovery": true
}
}{
"error_message": "Payment processing failed: No capture payment found for refund",
"errors": {
"payment": [
"No capture payment found for refund"
]
},
"meta": {
"request_id": "8c3f1e27-5b9d-4a60-9f2e-a7d4c1b6e938",
"timestamp": "2026-09-18T11:27:05Z"
}
}Headers
Optional client-generated key (UUID recommended) that makes a resend safe. A refund pays money out, so a client that resends after losing a response should send the same key. The key is stored with the refund it records, for that order. Resending the same request under the key answers with that refund and records nothing more; a different request under the key answers 422. The key must be 1 to 255 characters, or the request answers 422. A request that is refused records nothing, so the same key can send the corrected request.
Body
Shipping amount to refund. With type: shipping it is the amount refunded. With all, cv or qv it is added to the refund, whose total cannot pass what the order has left to refund. An all, cv or qv refund that resolves to partial ignores it, and so do the other types.
x >= 0Tax amount to refund. With type: tax it is the amount refunded. With all, cv or qv it is added to the refund, whose total cannot pass what the order has left to refund, though a cv or qv refund that also sends shipping uses only the shipping. An all, cv or qv refund that resolves to partial ignores it, and so do the other types.
x >= 0When true and the order was paid with a split card+points payment, refund redeemed points back to the customer. No-op if the order has no points portion or points have already been refunded. Most companies get back every redeemed point not yet returned. For a company whose refunds return points in proportion to the money refunded, type all still returns every point left, and any other refund, product_price included, returns the share of the base's card money it gives back, so tax and shipping return none when points apply to the subtotal. Shares are counted with the earlier refunds that asked for points back and rounded down to whole points. There, a refund that refunds no money, reverses no CV or QV and would return no points is refused with 422.
Whether this refund also ends the sale. When true and the refund leaves the order fully refunded and still unshipped, the order is cancelled. Omit the field to take the company default; send false to refund without cancelling, as for a goodwill refund on an order that should still ship. Sending true needs orders.delete.
curl --request POST \
--url https://api.fluid.app/api/refunds \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"order_id": 48213,
"type": "partial",
"amount": 10,
"reason": "Two reams arrived water-damaged.",
"restock_items": [
{
"item_id": 130577,
"quantity": 1
}
],
"cancel_order": false
}
'import requests
url = "https://api.fluid.app/api/refunds"
payload = {
"order_id": 48213,
"type": "partial",
"amount": 10,
"reason": "Two reams arrived water-damaged.",
"restock_items": [
{
"item_id": 130577,
"quantity": 1
}
],
"cancel_order": False
}
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({
order_id: 48213,
type: 'partial',
amount: 10,
reason: 'Two reams arrived water-damaged.',
restock_items: [{item_id: 130577, quantity: 1}],
cancel_order: false
})
};
fetch('https://api.fluid.app/api/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.fluid.app/api/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([
'order_id' => 48213,
'type' => 'partial',
'amount' => 10,
'reason' => 'Two reams arrived water-damaged.',
'restock_items' => [
[
'item_id' => 130577,
'quantity' => 1
]
],
'cancel_order' => false
]),
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.fluid.app/api/refunds"
payload := strings.NewReader("{\n \"order_id\": 48213,\n \"type\": \"partial\",\n \"amount\": 10,\n \"reason\": \"Two reams arrived water-damaged.\",\n \"restock_items\": [\n {\n \"item_id\": 130577,\n \"quantity\": 1\n }\n ],\n \"cancel_order\": false\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.fluid.app/api/refunds")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"order_id\": 48213,\n \"type\": \"partial\",\n \"amount\": 10,\n \"reason\": \"Two reams arrived water-damaged.\",\n \"restock_items\": [\n {\n \"item_id\": 130577,\n \"quantity\": 1\n }\n ],\n \"cancel_order\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/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 \"order_id\": 48213,\n \"type\": \"partial\",\n \"amount\": 10,\n \"reason\": \"Two reams arrived water-damaged.\",\n \"restock_items\": [\n {\n \"item_id\": 130577,\n \"quantity\": 1\n }\n ],\n \"cancel_order\": false\n}"
response = http.request(request)
puts response.read_body{
"refund": {
"id": 20958,
"order_id": 48213,
"amount": 10,
"reason": "Two reams arrived water-damaged.",
"refund_type": "partial",
"order": {
"id": 48213,
"first_name": "Holly",
"last_name": "Flax",
"email": "holly.flax@dundermifflin.com",
"order_number": "DM-10482",
"status": "awaiting_shipment",
"source": "admin",
"amount": "63.41",
"currency_code": "USD",
"token": "fo_7Kq2ZtW9xYbN4cRhJ3mLpV",
"refundable_amount": "53.41",
"order_status": "completed",
"fulfillment_status": "unfulfilled",
"financial_status": "partially_refunded",
"warehouse_id": 311,
"created_at": "2026-09-14T15:40:52Z",
"updated_at": "2026-09-16T10:04:37Z"
}
},
"meta": {
"request_id": "6d2f8b41-3e7a-4c95-b1d0-9a5c7e2f4b83",
"timestamp": "2026-09-16T10:04:37Z"
}
}{
"message": "The payment provider accepted the refund and will confirm it once it settles.",
"refund_status": "pending",
"meta": {
"request_id": "1f9c4a72-8d3b-4e06-a5e1-c2b7d9f0e364",
"timestamp": "2026-09-16T10:04:37Z"
}
}{
"message": "<string>",
"error": "<string>",
"error_message": "<string>",
"errors": "<string>",
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"code": "<string>",
"blocked_date": "2026-11-30",
"blocked_day": 30,
"allowed_date": "2026-11-28",
"scheduled_date": "2026-12-10",
"last_bill_date": "2026-11-10",
"requested_date": "2026-11-30",
"payment_uuid": "<string>",
"error_details": [
{
"code": "<string>",
"field": "<string>",
"payment": "not_attempted",
"message": "<string>"
}
],
"coded_recovery": true
}
}{
"message": "<string>",
"error": "<string>",
"error_message": "<string>",
"errors": "<string>",
"meta": {
"request_id": "<string>",
"timestamp": "2023-11-07T05:31:56Z",
"code": "<string>",
"blocked_date": "2026-11-30",
"blocked_day": 30,
"allowed_date": "2026-11-28",
"scheduled_date": "2026-12-10",
"last_bill_date": "2026-11-10",
"requested_date": "2026-11-30",
"payment_uuid": "<string>",
"error_details": [
{
"code": "<string>",
"field": "<string>",
"payment": "not_attempted",
"message": "<string>"
}
],
"coded_recovery": true
}
}{
"error_message": "Payment processing failed: No capture payment found for refund",
"errors": {
"payment": [
"No capture payment found for refund"
]
},
"meta": {
"request_id": "8c3f1e27-5b9d-4a60-9f2e-a7d4c1b6e938",
"timestamp": "2026-09-18T11:27:05Z"
}
}