> ## Documentation Index
> Fetch the complete documentation index at: https://docs.withgale.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Refund

> Refund a completed order (full or partial)

# Create Refund

Issue a full or partial refund for a completed order. Refunds are processed back to the original payment method (HSA/FSA card or regular card).

## Authentication

```bash theme={null}
Authorization: Bearer glm_test_YOUR_API_KEY
```

## Request Body

```json theme={null}
{
  "order_id": "ord_abc123xyz",
  "amount": 4235,
  "reason": "customer_request",
  "notes": "Customer requested refund due to delayed shipping",
  "metadata": {
    "support_ticket": "TICKET-12345"
  }
}
```

<Info>
  All monetary amounts are integers in cents (e.g., 4995 = \$49.95).
</Info>

## Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `order_id` | string | Yes | ID of the order to refund |
| `amount` | integer | No | Amount to refund in cents (omit for full refund) |
| `reason` | enum | Yes | Refund reason (see below) |
| `notes` | string | No | Additional notes (max 500 chars) |
| `metadata` | object | No | Custom key-value pairs |

<Info>
  **Finding Order ID**: You can get the `order_id` from:

  * Webhook event data (`order.created` event)
  * Order API response (`GET /api/v2/orders`)
  * Your database (store it when receiving `order.created` webhook)
  * Dashboard order details
</Info>

## Refund Reasons

| Reason | Description | Example Use Case |
| - | - | - |
| `customer_request` | Customer requested refund | Customer changed mind, no longer needed |
| `duplicate` | Duplicate payment | Customer accidentally paid twice |
| `fraudulent` | Fraudulent order | Detected fraudulent transaction |
| `product_unavailable` | Product out of stock | Item went out of stock after order |
| `damaged_product` | Product arrived damaged | Item damaged during shipping |
| `wrong_product` | Wrong product shipped | Fulfillment error, sent wrong item |
| `other` | Other reason | Any other reason (explain in `notes`) |

## Request

```bash theme={null}
POST /api/v2/refunds
```

## Response

```json theme={null}
{
  "success": true,
  "message": "Refund created",
  "data": {
    "id": "re_3Toy...",
    "order_id": "ord_abc123xyz",
    "amount": 4235,
    "reason": "customer_request",
    "notes": "Customer requested refund due to delayed shipping",
    "status": "succeeded",
    "refund_breakdown": {
      "hsa_amount": 2995,
      "regular_amount": 1240
    },
    "refunds": [
      { "refund_id": "re_3Toy...", "success": true }
    ],
    "metadata": {
      "support_ticket": "TICKET-12345"
    },
    "created_at": "2026-07-23T16:00:00Z"
  },
  "metadata": { "request_id": "..." }
}
```

Fields are returned under `data`.

## Response Fields

| Field | Type | Description |
| - | - | - |
| `id` | string | Processor refund reference |
| `status` | enum | Refund status: `pending`, `succeeded`, `failed`, `cancelled` |
| `refund_breakdown` | object | Refund amount per payment type (`hsa_amount`, `regular_amount`) |
| `refunds` | array | Per-payment refund results |

## Examples

### Full Refund

```bash theme={null}
curl -X POST https://api.withgale.com/api/v2/refunds \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": "ord_abc123xyz",
    "reason": "customer_request",
    "notes": "Customer not satisfied with product"
  }'
```

### Partial Refund

```bash theme={null}
curl -X POST https://api.withgale.com/api/v2/refunds \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": "ord_abc123xyz",
    "amount": 1000,
    "reason": "damaged_product",
    "notes": "Partial refund for damaged item"
  }'
```

## Refund Status

| Status | Description |
| - | - |
| `pending` | Refund initiated, processing |
| `succeeded` | Refund processed successfully |
| `failed` | Refund failed |
| `cancelled` | Refund cancelled |

## Split Card Refunds (Automatic)

**Gale automatically handles split card refunds.** You don't need to specify how much goes to each card.

When an order is paid with both HSA/FSA and regular card:

```json theme={null}
{
  "order": {
    "amounts": {
      "hsa_amount": 4995,       // Paid with HSA/FSA card
      "regular_amount": 895,    // Paid with regular card
      "total": 5890
    }
  }
}
```

### Full Refund

Just provide the `order_id` - Gale splits automatically:

```bash theme={null}
POST /api/v2/refunds
{
  "order_id": "ord_abc123",
  "reason": "customer_request"
}
```

**Response shows the breakdown:**

```json theme={null}
{
  "id": "ref_xyz789",
  "amount": 5890,
  "refund_breakdown": {
    "hsa_amount": 4995,       // Refunded to HSA/FSA card
    "regular_amount": 895     // Refunded to regular card
  }
}
```

### Partial Refund

Gale splits proportionally based on the original payment ratio:

```bash theme={null}
POST /api/v2/refunds
{
  "order_id": "ord_abc123",
  "amount": 2945,  // Partial refund
  "reason": "damaged_product"
}
```

**Response:**

```json theme={null}
{
  "id": "ref_xyz789",
  "amount": 2945,
  "refund_breakdown": {
    "hsa_amount": 2498,       // ~84.7% (same ratio as original)
    "regular_amount": 447     // ~15.3%
  }
}
```

**Key Points:**

* You only specify total `amount`
* Gale calculates the split automatically
* Each amount refunds to its original payment method
* Response shows the breakdown for your records

## Webhooks

Refunds trigger the following webhook events:

```json theme={null}
{
  "type": "refund.created",
  "data": {
    "id": "ref_xyz789",
    "order_id": "ord_abc123xyz",
    "amount": 4235,
    "status": "pending"
  }
}
```

```json theme={null}
{
  "type": "refund.succeeded",
  "data": {
    "id": "ref_xyz789",
    "order_id": "ord_abc123xyz",
    "status": "succeeded",
    "processed_at": "2025-10-18T16:05:00Z"
  }
}
```

See [Webhooks Reference](/developer-manual/webhooks) for all refund events.

## Errors

| Status Code | Error Code | Description |
| - | - | - |
| 400 | `invalid_amount` | Refund amount exceeds order total |
| 400 | `already_refunded` | Order already fully refunded |
| 400 | `invalid_state` | Order not in refundable state |
| 401 | `unauthorized` | Invalid or missing API key |
| 404 | `not_found` | Order not found |
| 422 | `validation_error` | Field validation failed |

### Amount Too Large

```json theme={null}
{
  "error": {
    "code": "invalid_amount",
    "message": "Refund amount exceeds order total",
    "details": {
      "requested": 10000,
      "maximum": 4235
    }
  }
}
```

### Already Refunded

```json theme={null}
{
  "error": {
    "code": "already_refunded",
    "message": "Order has already been fully refunded"
  }
}
```

### Invalid State

```json theme={null}
{
  "error": {
    "code": "invalid_state",
    "message": "Cannot refund order with payment status 'pending'"
  }
}
```

## Refund Timeline

| Payment Method | Typical Arrival Time |
| - | - |
| HSA/FSA Card | 5-10 business days |
| Regular Card | 5-10 business days |

**Note:** Actual timing depends on the customer's card issuer.

## Important Notes

<Warning>
  **Refunds cannot be undone.** Once a refund is processed, it cannot be reversed. Double-check order ID and amount before submitting.
</Warning>

<Info>
  **Partial refunds are supported.** You can issue multiple partial refunds up to the order total. The remaining refundable amount is tracked automatically.
</Info>

## Related Endpoints

* [Get Order](/api-reference/endpoint/get-order) - GET /api/v2/orders/{id}
* [List Orders](/api-reference/endpoint/list-orders) - GET /api/v2/orders

## Related Resources

* [Order Object](/api-reference/objects/order)
* [Webhooks Reference](/developer-manual/webhooks)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.