Create refund for items
Refunds specific order items by quantity through the payment provider that took the payment. The refund amount is computed from the items; pass cv and qv per item to deduct volume, and restock to return the units to inventory. Requires a Bearer token for a company admin with the refunds update permission, and cancel_order true also needs orders.delete. 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/items \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"order_id": 48213,
"reason": "Customer returned the unopened case.",
"order_items": [
{
"id": 130577,
"quantity": 1,
"restock": true
}
],
"cancel_order": false
}
'import requests
url = "https://api.fluid.app/api/refunds/items"
payload = {
"order_id": 48213,
"reason": "Customer returned the unopened case.",
"order_items": [
{
"id": 130577,
"quantity": 1,
"restock": True
}
],
"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,
reason: 'Customer returned the unopened case.',
order_items: [{id: 130577, quantity: 1, restock: true}],
cancel_order: false
})
};
fetch('https://api.fluid.app/api/refunds/items', 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/items",
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,
'reason' => 'Customer returned the unopened case.',
'order_items' => [
[
'id' => 130577,
'quantity' => 1,
'restock' => true
]
],
'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/items"
payload := strings.NewReader("{\n \"order_id\": 48213,\n \"reason\": \"Customer returned the unopened case.\",\n \"order_items\": [\n {\n \"id\": 130577,\n \"quantity\": 1,\n \"restock\": true\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/items")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"order_id\": 48213,\n \"reason\": \"Customer returned the unopened case.\",\n \"order_items\": [\n {\n \"id\": 130577,\n \"quantity\": 1,\n \"restock\": true\n }\n ],\n \"cancel_order\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/refunds/items")
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 \"reason\": \"Customer returned the unopened case.\",\n \"order_items\": [\n {\n \"id\": 130577,\n \"quantity\": 1,\n \"restock\": true\n }\n ],\n \"cancel_order\": false\n}"
response = http.request(request)
puts response.read_body{
"refund": {
"id": 20959,
"order_id": 48213,
"amount": 54.99,
"reason": "Customer returned the unopened case.",
"refund_type": "items",
"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": "8.42",
"order_status": "completed",
"fulfillment_status": "unfulfilled",
"financial_status": "partially_refunded",
"warehouse_id": 311,
"created_at": "2026-09-14T15:40:52Z",
"updated_at": "2026-09-18T11:27:05Z"
},
"order_item_refunds": [
{
"id": 31407,
"quantity": 1,
"amount": 54.99,
"item": {
"id": 130577,
"product_title": "Dunder Mifflin 20lb Copy Paper",
"title": "Dunder Mifflin 20lb Copy Paper",
"sku": "DM-20LB-CASE",
"quantity": 1,
"price": "54.99",
"total": "54.99"
}
}
]
},
"meta": {
"request_id": "3a8e1d64-7c2f-4b09-9e5d-b4f1a6c8d027",
"timestamp": "2026-09-18T11:27:05Z"
}
}{
"message": "The payment provider accepted the refund and will confirm it once it settles.",
"refund_status": "pending",
"meta": {
"request_id": "4b7e2c95-1a6d-4f38-8e0b-d5c9a3f7e216",
"timestamp": "2026-09-18T11:27:05Z"
}
}{
"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": "2e6b9d14-8f3a-4c71-b5e0-c9a7f2d4e183",
"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
When true and the order was paid partly or fully in points, return redeemed points to the customer. For a company whose refunds return points in proportion to the money refunded, an item refund returns the refunded lines' share of the spend, counted with the earlier refunds that asked for points back; otherwise every redeemed point not yet returned comes back.
curl --request POST \
--url https://api.fluid.app/api/refunds/items \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"order_id": 48213,
"reason": "Customer returned the unopened case.",
"order_items": [
{
"id": 130577,
"quantity": 1,
"restock": true
}
],
"cancel_order": false
}
'import requests
url = "https://api.fluid.app/api/refunds/items"
payload = {
"order_id": 48213,
"reason": "Customer returned the unopened case.",
"order_items": [
{
"id": 130577,
"quantity": 1,
"restock": True
}
],
"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,
reason: 'Customer returned the unopened case.',
order_items: [{id: 130577, quantity: 1, restock: true}],
cancel_order: false
})
};
fetch('https://api.fluid.app/api/refunds/items', 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/items",
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,
'reason' => 'Customer returned the unopened case.',
'order_items' => [
[
'id' => 130577,
'quantity' => 1,
'restock' => true
]
],
'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/items"
payload := strings.NewReader("{\n \"order_id\": 48213,\n \"reason\": \"Customer returned the unopened case.\",\n \"order_items\": [\n {\n \"id\": 130577,\n \"quantity\": 1,\n \"restock\": true\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/items")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"order_id\": 48213,\n \"reason\": \"Customer returned the unopened case.\",\n \"order_items\": [\n {\n \"id\": 130577,\n \"quantity\": 1,\n \"restock\": true\n }\n ],\n \"cancel_order\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/refunds/items")
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 \"reason\": \"Customer returned the unopened case.\",\n \"order_items\": [\n {\n \"id\": 130577,\n \"quantity\": 1,\n \"restock\": true\n }\n ],\n \"cancel_order\": false\n}"
response = http.request(request)
puts response.read_body{
"refund": {
"id": 20959,
"order_id": 48213,
"amount": 54.99,
"reason": "Customer returned the unopened case.",
"refund_type": "items",
"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": "8.42",
"order_status": "completed",
"fulfillment_status": "unfulfilled",
"financial_status": "partially_refunded",
"warehouse_id": 311,
"created_at": "2026-09-14T15:40:52Z",
"updated_at": "2026-09-18T11:27:05Z"
},
"order_item_refunds": [
{
"id": 31407,
"quantity": 1,
"amount": 54.99,
"item": {
"id": 130577,
"product_title": "Dunder Mifflin 20lb Copy Paper",
"title": "Dunder Mifflin 20lb Copy Paper",
"sku": "DM-20LB-CASE",
"quantity": 1,
"price": "54.99",
"total": "54.99"
}
}
]
},
"meta": {
"request_id": "3a8e1d64-7c2f-4b09-9e5d-b4f1a6c8d027",
"timestamp": "2026-09-18T11:27:05Z"
}
}{
"message": "The payment provider accepted the refund and will confirm it once it settles.",
"refund_status": "pending",
"meta": {
"request_id": "4b7e2c95-1a6d-4f38-8e0b-d5c9a3f7e216",
"timestamp": "2026-09-18T11:27:05Z"
}
}{
"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": "2e6b9d14-8f3a-4c71-b5e0-c9a7f2d4e183",
"timestamp": "2026-09-18T11:27:05Z"
}
}