Refunds API
List and create refunds for an order, or list the store’s refunds across
all its orders. Refunds are recorded against the order and reflected in its
refund_status. A refund can also put order lines back in stock.
See Webhooks to be told when a refund is created.
Endpoints
- List Store Refunds -
GET /api/v1/refunds/ - List Refunds -
GET /api/v1/orders/{order_id}/refunds/ - Create Refund -
POST /api/v1/orders/{order_id}/refunds/
List Store Refunds
Retrieve a paginated list of the store’s refunds across all its orders,
oldest first. A tool that keeps its own record of refunds, such as one that
issues credit notes, can catch up on the refunds it hasn’t seen by paging
after the last one it saw, and start its first sync from a date with
created_after.
Endpoint: GET /api/v1/refunds/
Scope Required: refunds:read
Query Parameters
| Parameter | Type | Description |
|---|---|---|
after |
string | Cursor for pagination. Refund public ID to return refunds after |
before |
string | Cursor for pagination. Refund public ID to return refunds before, for paging back |
limit |
integer | Number of refunds to return (default 10, max 100) |
created_after |
string | Only refunds created at or after this RFC 3339 time, such as 2026-01-01T00:00:00Z |
Every page comes oldest first, whether you page forward with after or back
with before. The response reports has_more, which is true when another
page lies beyond this one in the direction you’re paging.
after and before cannot be used together. created_after combines with
either.
Request
curl -X GET "https://studio.easel.engineering/api/v1/refunds/?after=ref_d0b6fmv28q6vn14peun0" \
-H "Authorization: Bearer your_api_key_here"
Response
Status: 200 OK
{
"data": [
{
"id": "ref_d0b6fmv28q6vn14peun1",
"number": 1,
"order_id": "ord_d0b6fmv28q6vn14peun1",
"amount": 2598,
"tax": 0,
"taxes": [],
"gift_card_amount": 0,
"reason": null,
"restocks": [],
"created_at": "2023-01-04T08:30:00Z"
}
],
"has_more": false
}
Error Responses
Invalid query parameter
Status: 400 Bad Request
A malformed after or before, both at once, an invalid limit, or a
created_after that isn’t an RFC 3339 time returns INVALID_REQUEST_ERROR,
with the offending parameter named in the error message:
{
"error": "Invalid 'created_after' parameter: must be an RFC 3339 time, such as 2026-01-01T00:00:00Z",
"code": "INVALID_REQUEST_ERROR"
}
Cursor not found
An after or before that names no refund of the store.
Status: 404 Not Found
{
"error": "Resource not found",
"code": "NOT_FOUND"
}
List Refunds
Retrieve the refunds for an order. This endpoint is not paginated.
Endpoint: GET /api/v1/orders/{order_id}/refunds/
Scope Required: refunds:read
Request
curl -X GET "https://studio.easel.engineering/api/v1/orders/ord_d0b6fmv28q6vn14peun0/refunds/" \
-H "Authorization: Bearer your_api_key_here"
Response
Status: 200 OK
{
"data": [
{
"id": "ref_d0b6fmv28q6vn14peun0",
"number": 1,
"order_id": "ord_d0b6fmv28q6vn14peun0",
"amount": 500,
"tax": 34,
"taxes": [
{
"country": "US",
"region": "CA",
"name": "Sales tax",
"rate": "7.25",
"tax": 34
}
],
"gift_card_amount": 0,
"reason": "Damaged item",
"restocks": [
{
"order_variant_id": "orv_d0b6fmv28q6vn14peun0",
"quantity": 1
}
],
"created_at": "2023-01-03T10:00:00Z"
}
]
}
Create Refund
Refund an amount against an order. The amount must be greater than zero and is
denominated in the store’s smallest currency unit. When the order collected tax,
the refund gives back the same share of each jurisdiction’s tax as it does of
the order’s total, which the refund’s tax reports.
Orders that bought gift cards
A refund of an order that bought gift cards comes out of the rest of the order first: its other lines, shipping, gift option, and tax. Refunding a returned item never touches a gift card. Past that, the refund gives back the cards’ price and takes it back off them, in the same step, so it can’t be spent again.
By default it gives back only what’s left unspent on the cards, since the
buyer already had the rest. Refunding more is a 400 with INVALID_FIELD
on amount. With refund_spent_gift_cards set to true it can give back
everything not yet refunded, and empties and disables every card the order
bought. Each card’s transactions show
what a refund took back as returned.
Gift cards carry no tax, so only the rest of the order gives back any.
To put units back in stock, list them in restocks. Restocking is opt-in:
without restocks the refund changes no stock, which suits an amount-only
refund such as a goodwill credit. Each restocked unit adds one to the
variant’s stock and is recorded on the refund. Across all of an order’s
refunds, a line can be restocked at most as many units as were ordered, so
retrying a request can’t put the same units back twice. Digital products have
no stock and can’t be restocked. A restock that brings a variant back into
stock emails the shoppers on its restock waitlist,
the same as raising the stock directly.
Restocks are checked before any money moves: an invalid restock rejects the whole request and nothing is refunded.
Refunds made outside Easel, such as in the Stripe dashboard, or lost disputes are recorded on the order from Stripe’s webhook but never restock, since Easel can’t know whether the goods came back.
When a gift card paid part of the order, the refund goes back to the buyer’s card first, up to what was charged to it, and the rest back onto the gift card, which needs no Stripe refund. A refund of an order a gift card paid in full goes wholly back onto the card.
Endpoint: POST /api/v1/orders/{order_id}/refunds/
Scope Required: refunds:write
Request Body
{
"amount": 500,
"reason": "Damaged item",
"restocks": [
{
"order_variant_id": "orv_d0b6fmv28q6vn14peun0",
"quantity": 1
}
]
}
| Field | Type | Required | Description |
|---|---|---|---|
amount |
integer | Yes | Refund amount in the store’s smallest currency unit (must be greater than zero) |
reason |
string | No | Reason for the refund |
restocks |
array | No | Order lines to put back in stock (see below). Omit to restock nothing |
refund_spent_gift_cards |
boolean | No | Refund the full price of gift cards the order bought, even what was spent of them, which empties and disables them. Omitted or false, a refund gives back only what’s left on them (see below) |
Each restocks entry:
| Field | Type | Required | Description |
|---|---|---|---|
order_variant_id |
string | Yes | The line’s order_variant_id from the order’s items; each line at most once |
quantity |
integer | Yes | Units to put back in stock; 0 restocks nothing. At most the line’s quantity less any units earlier refunds restocked |
Request
curl -X POST "https://studio.easel.engineering/api/v1/orders/ord_d0b6fmv28q6vn14peun0/refunds/" \
-H "Authorization: Bearer your_api_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-12345" \
-d '{
"amount": 500,
"reason": "Damaged item"
}'
Response
Status: 201 Created
The response body is empty.
Error Responses
Invalid amount
Status: 400 Bad Request
{
"error": "Amount must be greater than zero",
"code": "INVALID_FIELD"
}
Amount exceeds the refundable balance, or the order isn’t paid
Status: 400 Bad Request
{
"error": "Amount exceeds the order's refundable balance",
"code": "INVALID_FIELD"
}
Invalid restock
A restock that names a line not on the order, names a line twice, has a negative quantity, names a digital product, or asks for more units than the line can still take.
Status: 400 Bad Request
{
"error": "Restock quantity exceeds the units ordered, less any already restocked",
"code": "INVALID_FIELD"
}
Order not found
Status: 404 Not Found
{
"error": "Resource not found",
"code": "NOT_FOUND"
}
Refund Object
Fields
| Field | Type | Description |
|---|---|---|
id |
string | Refund public ID |
number |
integer | Refund number within the order |
order_id |
string | Public ID of the refunded order |
amount |
integer | Refund amount in the store’s smallest currency unit |
tax |
integer | The part of amount that gave back the order’s tax, in proportion to the share of the order refunded |
taxes |
array | tax broken down by the order’s jurisdictions, as the order’s taxes name them; empty when the order collected no tax. See Refund Tax Object |
gift_card_amount |
integer | Part of amount put back on the order’s gift card rather than its card |
reason |
string|null | Reason for the refund |
restocks |
array | Units the refund put back in stock; empty when it restocked nothing |
created_at |
string | ISO 8601 timestamp |
Refund Tax Object
| Field | Type | Description |
|---|---|---|
country |
string | ISO 3166-1 alpha-2 country code |
region |
string|null | Subdivision code, such as CA for California, or null for a whole country |
name |
string | Name of the tax, such as VAT or Sales tax |
rate |
string | Rate as a percentage, such as "23" |
tax |
integer | Tax given back in the store’s smallest currency unit |
Refund Restock Object
| Field | Type | Description |
|---|---|---|
order_variant_id |
string | The restocked order line |
quantity |
integer | Units put back in stock |