Hydra is now in beta|Get started free|Follow our journey on X.com

Refunds API
On this page

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_items with per-item quantities and amounts. The total refund is calculated as sum(line item amounts) + shipping_refund_amount - restocking_fee.
  • Goodwill refund: Provide a flat amount without 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_prefix on 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