On this page
- Endpoints
- List refunds
- Query parameters
- Request
- Response 200
- Get a refund
- Path parameters
- Query parameters
- Request
- Response 200
- Create a refund
- Path parameters
- Request body
- Request (line-item refund)
- Request (goodwill refund)
- Response 201
- Retry a failed refund
- Path parameters
- Request
- Response 200
- Download credit note PDF
- Path parameters
- Example request
- List refunds for an order
- Path parameters
- Request
- Response 200
- Webhooks
- Refund statuses
- The refund object
- Refund line item object
Refunds
A refund reverses all or part of a payment for an order. Refunds can include per-line-item amounts, shipping cost reversal, and restocking fees. For orders paid via Stripe, the payment reversal happens automatically. Refunds are permanent financial records and cannot be deleted.
Refunds and returns are separate but linkable. A refund tracks the financial flow (money going back); a return tracks the physical flow (items coming back). Either can exist independently — a goodwill refund needs no return, and a replacement return needs no refund.
Base URL: https://api.hydrajs.dev
Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/v1/refunds |
Secret | List all refunds |
GET |
/v1/refunds/{id} |
Secret | Get a refund |
POST |
/v1/orders/{id}/refunds |
Secret | Create a refund for an order |
POST |
/v1/refunds/{id}/retry |
Secret | Retry a failed refund |
GET |
/v1/refunds/{id}/credit-note |
Secret | Download credit note PDF |
GET |
/v1/orders/{id}/refunds |
Secret | List refunds for a specific order |
List refunds
GET /v1/refunds
Returns a cursor-paginated list of refunds. Supports filtering by order, return, and status.
Query parameters
| Parameter | Type | Description |
|---|---|---|
limit |
integer | Results per page (1–100, default 20) |
cursor |
string | Pagination cursor from a previous response |
order_id |
string | Filter by order ID |
return_id |
string | Filter by linked return ID |
status |
string | Filter by status: pending, succeeded, failed |
sort |
string | Sort field: created_at (default), updated_at |
order |
string | Sort direction: asc, desc (default) |
Request
curl "https://api.hydrajs.dev/v1/refunds?status=succeeded&limit=10" \
-H "Authorization: Bearer sk_live_YOUR_KEY"
Response 200
{
"data": [
{
"id": "rfd_abc123def456ghi789",
"order_id": "ord_abc123def456ghi789",
"return_id": null,
"status": "succeeded",
"amount": 2500,
"currency": "usd",
"reason": "goodwill",
"reason_note": "Late delivery compensation",
"refund_shipping": false,
"shipping_refund_amount": 0,
"restocking_fee": 0,
"credit_note_number": null,
"staff_note": null,
"processed_at": "2026-09-03T14:30:00.000Z",
"failed_at": null,
"failure_reason": null,
"metadata": {},
"created_at": "2026-09-03T14:30:00.000Z",
"updated_at": "2026-09-03T14:30:00.000Z"
}
],
"pagination": {
"cursor": "eyJ0IjoiMjAyNi...",
"has_more": false,
"total": 1
}
}
Get a refund
GET /v1/refunds/{id}
Returns a single refund by ID. Use ?expand=line_items to include per-item refund details.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id |
string | Refund ID (prefix: rfd_) |
Query parameters
| Parameter | Type | Description |
|---|---|---|
expand |
string | Comma-separated: line_items |
fields |
string | Comma-separated field names for sparse response |
Request
curl "https://api.hydrajs.dev/v1/refunds/rfd_abc123def456ghi789?expand=line_items" \
-H "Authorization: Bearer sk_live_YOUR_KEY"
Response 200
{
"data": {
"id": "rfd_abc123def456ghi789",
"order_id": "ord_abc123def456ghi789",
"return_id": null,
"status": "succeeded",
"amount": 3000,
"currency": "usd",
"reason": "return",
"reason_note": "Customer received wrong size",
"refund_shipping": true,
"shipping_refund_amount": 500,
"restocking_fee": 0,
"credit_note_number": "CN-0001",
"staff_note": "Approved by Alice",
"processed_at": "2026-09-03T14:30:00.000Z",
"failed_at": null,
"failure_reason": null,
"metadata": {},
"line_items": [
{
"id": "rfli_abc123def456ghi78",
"order_line_item_id": "li_abc123def456ghi789",
"quantity": 1,
"amount": 2500,
"created_at": "2026-09-03T14:30:00.000Z",
"updated_at": "2026-09-03T14:30:00.000Z"
}
],
"created_at": "2026-09-03T14:30:00.000Z",
"updated_at": "2026-09-03T14:30:00.000Z"
}
}
Create a refund
POST /v1/orders/{id}/refunds
Issues a full or partial refund for an order. Supports two modes:
- Line-item refund: Provide
line_itemswith per-item quantities and amounts. The total refund is calculated assum(line item amounts) + shipping_refund_amount - restocking_fee. - Goodwill refund: Provide a flat
amountwithout line items. Used for compensation, duplicate charges, or order errors.
For orders paid via Stripe, the payment reversal happens automatically. For manual/cash orders, the refund is recorded immediately as succeeded.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id |
string | Order ID (prefix: ord_) |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | Yes | return, goodwill, order_error, shipping_issue, duplicate, other |
line_items |
array | No | Line items to refund (omit for goodwill refunds) |
line_items[].order_line_item_id |
string | Yes | Order line item ID |
line_items[].quantity |
integer | Yes | Quantity to refund (min 1) |
line_items[].amount |
integer | Yes | Refund amount for this line in cents |
amount |
integer | No | Flat refund amount in cents (for goodwill refunds) |
return_id |
string | No | Link to a return (must be in received or closed status) |
reason_note |
string | No | Free-text explanation (max 1000 chars) |
refund_shipping |
boolean | No | Whether to include shipping in the refund (default false) |
shipping_refund_amount |
integer | No | Shipping refund in cents (default 0) |
restocking_fee |
integer | No | Restocking fee in cents, subtracted from total (default 0) |
staff_note |
string | No | Internal note, not shown to customers (max 2000 chars) |
metadata |
object | No | Arbitrary key-value pairs |
⚠Refund amount limits
The total refunded for an order (sum of all succeeded refund amounts) cannot exceed the order total. Attempting to over-refund returns a 400 error. Per-line-item quantities cannot exceed the original quantity minus already-refunded quantities.
Request (line-item refund)
curl -X POST "https://api.hydrajs.dev/v1/orders/ord_abc123def456ghi789/refunds" \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"line_items": [
{
"order_line_item_id": "li_abc123def456ghi789",
"quantity": 1,
"amount": 2500
}
],
"reason": "return",
"reason_note": "Customer received wrong size",
"refund_shipping": true,
"shipping_refund_amount": 500,
"staff_note": "Approved by Alice"
}'
Request (goodwill refund)
curl -X POST "https://api.hydrajs.dev/v1/orders/ord_abc123def456ghi789/refunds" \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 500,
"reason": "goodwill",
"reason_note": "Compensation for late delivery"
}'
Response 201
{
"data": {
"id": "rfd_abc123def456ghi789",
"order_id": "ord_abc123def456ghi789",
"return_id": null,
"status": "succeeded",
"amount": 3000,
"currency": "usd",
"reason": "return",
"reason_note": "Customer received wrong size",
"refund_shipping": true,
"shipping_refund_amount": 500,
"restocking_fee": 0,
"credit_note_number": null,
"staff_note": "Approved by Alice",
"processed_at": "2026-09-03T14:30:00.000Z",
"failed_at": null,
"failure_reason": null,
"metadata": {},
"line_items": [
{
"id": "rfli_abc123def456ghi78",
"order_line_item_id": "li_abc123def456ghi789",
"quantity": 1,
"amount": 2500,
"created_at": "2026-09-03T14:30:00.000Z",
"updated_at": "2026-09-03T14:30:00.000Z"
}
],
"created_at": "2026-09-03T14:30:00.000Z",
"updated_at": "2026-09-03T14:30:00.000Z"
}
}
Retry a failed refund
POST /v1/refunds/{id}/retry
Retries a previously failed Stripe refund. Only refunds with failed status can be retried. The refund transitions from failed back to pending, then to succeeded or failed depending on the Stripe result.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id |
string | Refund ID (prefix: rfd_) |
Request
curl -X POST "https://api.hydrajs.dev/v1/refunds/rfd_abc123def456ghi789/retry" \
-H "Authorization: Bearer sk_live_YOUR_KEY"
Response 200
Returns the updated refund object with the new status.
Download credit note PDF
GET /v1/refunds/{id}/credit-note
Generates and returns a PDF credit note for the refund. The credit note number is permanently assigned on first access and returned in subsequent GET /v1/refunds/{id} responses as credit_note_number.
Auth: Secret key required
Response: application/pdf binary with Content-Disposition: inline
Path parameters
| Parameter | Type | Description |
|---|---|---|
id |
string | Refund ID (prefix: rfd_) |
Example request
curl https://api.hydrajs.dev/v1/refunds/rfd_abc123/credit-note \
-H "Authorization: Bearer sk_live_..." \
-o credit-note.pdf
The generated PDF includes:
- Store business info (name, address, tax ID) from project settings
- Credit note number (auto-incrementing, customizable prefix via
credit_note_prefixon store settings) - Reference to the original order number and invoice number
- Refunded line items with quantities and amounts
- Shipping refund and restocking fee (if applicable)
- Total credit amount
- Refund reason
- Brand logo (if configured)
Test mode refunds produce a watermarked “DRAFT” credit note with a DRAFT- prefix on the number.
ℹCredit notes vs invoices
Invoices are immutable legal documents. When a refund is issued, a separate credit note is generated rather than modifying the original invoice. This follows standard accounting practice used by Stripe, Shopify, and Xero.
List refunds for an order
GET /v1/orders/{id}/refunds
Returns all refunds for a specific order.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id |
string | Order ID (prefix: ord_) |
Request
curl "https://api.hydrajs.dev/v1/orders/ord_abc123def456ghi789/refunds" \
-H "Authorization: Bearer sk_live_YOUR_KEY"
Response 200
{
"data": [
{
"id": "rfd_abc123def456ghi789",
"order_id": "ord_abc123def456ghi789",
"status": "succeeded",
"amount": 2500,
"currency": "usd",
"reason": "goodwill",
"created_at": "2026-09-03T14:30:00.000Z",
"updated_at": "2026-09-03T14:30:00.000Z"
}
]
}
Webhooks
Refund changes fire the following webhook events:
| Event | Trigger |
|---|---|
refund.created |
A refund is created |
refund.succeeded |
Stripe confirms the payment reversal |
refund.failed |
Stripe rejects the payment reversal |
The existing order.refunded event also fires when the order’s financial_status transitions to refunded (all money returned).
See Webhooks for subscription setup.
Refund statuses
| Status | Description |
|---|---|
pending |
Refund created, Stripe processing |
succeeded |
Payment reversal confirmed |
failed |
Payment reversal rejected (can be retried) |
pending ──> succeeded
│
└──> failed ──> pending (retry)
ℹCard refunds
Card refunds via Stripe are typically synchronous — the pending state is transient
and resolves to succeeded or failed within the same request. Bank transfer refunds
may stay pending longer.
The refund object
| Field | Type | Description |
|---|---|---|
id |
string | Unique ID (prefix: rfd_) |
order_id |
string | The order being refunded |
return_id |
string | null | Linked return ID (null for standalone refunds) |
status |
string | pending, succeeded, failed |
amount |
integer | Total refund amount in cents |
currency |
string | ISO 4217 currency code (e.g. usd) |
reason |
string | Refund reason (see create endpoint) |
reason_note |
string | null | Free-text explanation |
refund_shipping |
boolean | Whether shipping is included in the refund |
shipping_refund_amount |
integer | Shipping refund in cents |
restocking_fee |
integer | Restocking fee in cents |
credit_note_number |
string | null | Assigned when credit note is first generated (e.g. CN-0001). null until GET /v1/refunds/{id}/credit-note is called. |
staff_note |
string | null | Internal note (not shown to customers) |
processed_at |
string | null | ISO 8601 timestamp when Stripe confirmed |
failed_at |
string | null | ISO 8601 timestamp when refund failed |
failure_reason |
string | null | Why the refund failed (from Stripe) |
metadata |
object | Arbitrary key-value pairs |
line_items |
array | Refund line items (when expanded, see below) |
created_at |
string | ISO 8601 timestamp |
updated_at |
string | ISO 8601 timestamp |
Refund line item object
| Field | Type | Description |
|---|---|---|
id |
string | Unique ID (prefix: rfli_) |
order_line_item_id |
string | The order line item being refunded |
quantity |
integer | Number of units refunded |
amount |
integer | Refund amount for this line in cents |
created_at |
string | ISO 8601 timestamp |
updated_at |
string | ISO 8601 timestamp |