> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.paymentkit.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.paymentkit.com/_mcp/server.

# Issue a refund

A refund returns captured funds to the customer who paid them. You can refund a charge by its
payment intent or by the invoice it paid, refund the full amount or only part of it, and record
refunds that were already processed outside PaymentKit.

# Create a refund

A refund always targets a single succeeded charge. You identify that charge in one of two ways:

* `payment_intent_id` — refund the latest succeeded charge on a payment intent.
* `invoice_id` — refund the charge that paid an invoice. PaymentKit resolves the payment
  allocation for you. (If the invoice was paid by more than one payment intent, refund by
  `payment_intent_id` instead.)

Provide at least one of these. If you supply both, `payment_intent_id` takes precedence.

1. Open the **Payment** or **Invoice** you want to refund
2. Click **Refund**
3. Enter a full or partial amount and choose a reason
4. Click **Refund** to send it to the processor

The refund appears immediately with a **Pending** status and updates to **Succeeded** once
the processor confirms it.

```bash
curl -X POST https://api.paymentkit.com/api/{account_id}/payments/refunds \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: refund-order-4821" \
-d '{
  "payment_intent_id": "pi_abc123",
  "amount_atom": 1500,
  "reason": "requested_by_customer"
}'
```

Omit `amount_atom` to refund the full refundable amount.

## Request fields

| Field                  | Type      | Description                                                                                                                                                                                         |
| ---------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `payment_intent_id`    | `string`  | The payment intent to refund. Required unless `invoice_id` is provided.                                                                                                                             |
| `invoice_id`           | `string`  | An invoice to refund. PaymentKit resolves the charge that paid it. Required unless `payment_intent_id` is provided.                                                                                 |
| `amount_atom`          | `integer` | Amount to refund, in the smallest currency unit (e.g. cents). Must be greater than `0`. Defaults to the full refundable amount.                                                                     |
| `currency`             | `string`  | Currency code (e.g. `USD`). Defaults to the charge's currency; if provided, it must match.                                                                                                          |
| `reason`               | `string`  | One of `manual`, `duplicate`, `fraudulent`, `requested_by_customer`, `expired_uncaptured_charge`, `manual_out_of_band`. Defaults to `manual`.                                                       |
| `refunded_out_of_band` | `boolean` | Set `true` to record a refund you already processed elsewhere (e.g. in the processor's dashboard). No processor call is made and the refund is marked `succeeded` immediately. Defaults to `false`. |
| `metadata`             | `object`  | Free-form JSON, up to 10 KB.                                                                                                                                                                        |

Send an `Idempotency-Key` header on every create request. If the call is retried with the same
key, PaymentKit returns the original refund instead of issuing a duplicate.

# The refund object

```json
{
  "id": "re_live_0a1b2c3d4e5f6g7h",
  "account_id": "acc_live_...",
  "charge_id": "ch_live_...",
  "amount_atom": 1500,
  "currency": "USD",
  "status": "succeeded",
  "reason": "requested_by_customer",
  "processor_refund_id": "re_3Nz...",
  "failure_code": null,
  "failure_message": null,
  "processed_at": "2026-06-17T14:05:00Z",
  "metadata": null,
  "created_at": "2026-06-17T14:04:58Z",
  "updated_at": "2026-06-17T14:05:00Z"
}
```

| Field                              | Description                                                                           |
| ---------------------------------- | ------------------------------------------------------------------------------------- |
| `id`                               | Refund identifier, prefixed `re_`.                                                    |
| `charge_id`                        | The charge that was refunded.                                                         |
| `amount_atom` / `currency`         | The refunded amount and its currency.                                                 |
| `status`                           | Lifecycle status — see below.                                                         |
| `reason`                           | The reason supplied at creation, if any.                                              |
| `processor_refund_id`              | The processor's own refund ID. `null` for out-of-band refunds or while still pending. |
| `failure_code` / `failure_message` | Populated when `status` is `failed`.                                                  |
| `processed_at`                     | When the processor finished the refund. `null` while pending.                         |

## Refund statuses

| Status            | Meaning                                                                              |
| ----------------- | ------------------------------------------------------------------------------------ |
| `pending`         | Created and awaiting the processor's response.                                       |
| `succeeded`       | Funds returned to the customer (or recorded out-of-band). Terminal.                  |
| `failed`          | The processor rejected the refund. See `failure_code` / `failure_message`. Terminal. |
| `requires_action` | Additional action is required before the refund can complete (rare). Non-terminal.   |
| `cancelled`       | Cancelled before completion — from `pending` or `requires_action`. Terminal.         |

# Look up refunds

| Method | Path                                                               | Returns                                      |
| ------ | ------------------------------------------------------------------ | -------------------------------------------- |
| `GET`  | `/api/{account_id}/payments/refunds/{refund_id}`                   | A single refund                              |
| `GET`  | `/api/{account_id}/payments/refunds/by_charge/{charge_id}`         | All refunds for a charge (paginated)         |
| `GET`  | `/api/{account_id}/payments/refunds/by_intent/{payment_intent_id}` | All refunds for a payment intent (paginated) |

```bash
curl https://api.paymentkit.com/api/{account_id}/payments/refunds/by_intent/pi_abc123 \
  -H "Authorization: Bearer sk_live_..."
```

# Rules and limits

* **The charge must have succeeded.** You can only refund a charge in the `succeeded` state.
* **You cannot refund more than the refundable amount** — the captured amount minus any refunds
  that have already succeeded. Fully-refunded charges have a refundable amount of `0`.
* **Partial refunds** are allowed as long as the running total stays within the refundable
  amount. Issue several partial refunds against the same charge until it is fully refunded.
* **Refunding an invoice payment** reduces the paid amount on the linked invoice(s) accordingly.

# Webhook events

| Event            | Trigger                                                                 |
| ---------------- | ----------------------------------------------------------------------- |
| `refund.created` | A refund was created.                                                   |
| `refund.updated` | A refund field changed — including the transition to `succeeded`.       |
| `refund.failed`  | The refund was rejected. Includes `failure_code` and `failure_message`. |

There is no dedicated `refund.succeeded` event. A successful refund surfaces as `refund.updated` carrying the new `succeeded` status.