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

# Handling Refunds

> Process refunds for your customers

# Handling Refunds

This guide explains how to process refunds through the SwiftPay API.

## How Refunds Work

When you create a refund:

1. The refund is created in `pending` status
2. We process the refund with our payment processor
3. The refund status updates to `completed` or `failed`
4. Webhooks notify you of the status change

<Note>Refunds can only be created for `completed` checkout sessions.</Note>

## Creating a Refund

### Full Refund

To refund the full amount of a payment:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.swiftpay.cx/api/refunds \
    -H "Authorization: Bearer mp_live_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "checkoutSessionId": "sess_abc123",
      "reason": "Customer requested refund"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://api.swiftpay.cx/api/refunds", {
     method: "POST",
     headers: {
        Authorization: "Bearer mp_live_your_api_key",
        "Content-Type": "application/json",
     },
     body: JSON.stringify({
        checkoutSessionId: "sess_abc123",
        reason: "Customer requested refund",
     }),
  });

  const refund = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.swiftpay.cx/api/refunds',
      headers={
          'Authorization': 'Bearer mp_live_your_api_key',
          'Content-Type': 'application/json'
      },
      json={
          'checkoutSessionId': 'sess_abc123',
          'reason': 'Customer requested refund'
      }
  )

  refund = response.json()
  ```
</CodeGroup>

### Response

```json theme={null}
{
   "success": true,
   "data": {
      "id": "ref_xyz789",
      "checkoutSessionId": "sess_abc123",
      "amountInCents": 2999,
      "reason": "Customer requested refund",
      "status": "pending",
      "requestedAt": "2024-01-15T10:30:00Z"
   }
}
```

## Refund Statuses

| Status       | Description                         |
| ------------ | ----------------------------------- |
| `pending`    | Refund created, awaiting processing |
| `processing` | Refund is being processed           |
| `completed`  | Refund successful, funds returned   |
| `failed`     | Refund failed                       |

## Checking Refund Status

### Get a Specific Refund

```bash theme={null}
curl https://api.swiftpay.cx/api/refunds/ref_xyz789 \
  -H "Authorization: Bearer mp_live_your_api_key"
```

### List All Refunds

```bash theme={null}
curl "https://api.swiftpay.cx/api/refunds?page=1&limit=20" \
  -H "Authorization: Bearer mp_live_your_api_key"
```

### Filter by Transaction

```bash theme={null}
curl "https://api.swiftpay.cx/api/refunds?transactionId=sess_abc123" \
  -H "Authorization: Bearer mp_live_your_api_key"
```

## Refund Webhooks

Subscribe to refund events to get real-time updates:

| Event              | Description                   |
| ------------------ | ----------------------------- |
| `refund.completed` | Refund processed successfully |
| `refund.failed`    | Refund failed                 |

### Example Webhook Payload

```json theme={null}
{
   "id": "evt_abc123",
   "type": "refund.completed",
   "created": 1640995200,
   "data": {
      "refundId": "ref_xyz789",
      "sessionId": "sess_abc123",
      "amount": 2999,
      "status": "completed",
      "timestamp": "2024-01-15T10:35:00Z"
   }
}
```

## Refunds and Reserves

<Warning>
  If a refund exceeds your available balance, funds will be drawn from your
  rolling reserves.
</Warning>

When processing a refund:

1. We first check your **available balance**
2. If insufficient, we consume from your **active reserves** (oldest first)
3. If still insufficient, the refund will fail

You can view your balance breakdown via the Balance API:

```bash theme={null}
curl https://api.swiftpay.cx/api/balance \
  -H "Authorization: Bearer mp_live_your_api_key"
```

```json theme={null}
{
   "success": true,
   "data": {
      "availableBalance": 50000,
      "reservedBalance": 15000,
      "totalBalance": 65000,
      "pendingWithdrawals": 0
   }
}
```

## Processing Time

| Type        | Processing Time    |
| ----------- | ------------------ |
| Card Refund | 5-10 business days |

<Note>
  Refund processing time depends on the customer's bank and card network.
</Note>

## Best Practices

<AccordionGroup>
  <Accordion title="Always Provide a Reason" icon="message">
    Include a clear reason for every refund. This helps with:

    * Internal tracking
    * Customer communication
    * Dispute resolution
  </Accordion>

  <Accordion title="Check Balance First" icon="wallet">
    Before creating a refund, check your available balance to ensure sufficient funds:

    ```javascript theme={null}
    const balance = await getBalance();
    if (balance.availableBalance + balance.reservedBalance < refundAmount) {
      throw new Error('Insufficient funds for refund');
    }
    ```
  </Accordion>

  <Accordion title="Use Webhooks" icon="webhook">
    Don't poll for refund status. Use webhooks to get real-time updates when a refund completes or fails.
  </Accordion>

  <Accordion title="Handle Failures Gracefully" icon="shield">
    If a refund fails, notify your customer and provide alternative resolution options.
  </Accordion>
</AccordionGroup>

## Common Issues

### Insufficient Balance

```json theme={null}
{
   "success": false,
   "error": {
      "type": "validation_error",
      "message": "Insufficient balance for refund",
      "requestId": "req_abc123"
   }
}
```

**Solution**: Wait for more payments to settle or contact support.

### Session Not Completed

```json theme={null}
{
   "success": false,
   "error": {
      "type": "validation_error",
      "message": "Can only refund completed sessions",
      "requestId": "req_abc123"
   }
}
```

**Solution**: Refunds can only be created for sessions with status `completed`.

### Already Refunded

```json theme={null}
{
   "success": false,
   "error": {
      "type": "conflict_error",
      "message": "This session has already been refunded",
      "requestId": "req_abc123"
   }
}
```

**Solution**: Each session can only be refunded once.
