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

# Refunds, captures, and cancellations

> Move money back, capture authorized payments, and cancel intents.

Checkout handles the buyer flow. Refunds handle money moving back after a payment has completed.

## Create a refund

Create refunds from your backend or support tooling. Send an idempotency key so duplicate button clicks or retries do not create duplicate refunds.

On Node, `npm install @ionicfi/sdk` gives you typed calls for every request
below; the [server SDK](/sdks/server) guide has details. Each example also
shows raw curl.

<CodeGroup>
  ```ts TypeScript theme={null}
  import { Ionic } from "@ionicfi/sdk";

  const ionic = new Ionic({ token: process.env.IONIC_SECRET_KEY });

  const refund = await ionic.refunds.create({
    "Idempotency-Key": "refund_order_123",
    payment_intent_id: "pi_000000000000000000000000",
    amount: 1500,
    reason: "requested_by_customer",
  });
  ```

  ```bash curl theme={null}
  curl https://api.ionicfi.com/v1/refunds \
    -H "Authorization: Bearer sk_v1_test_..." \
    -H "Idempotency-Key: refund_order_123" \
    -H "Content-Type: application/json" \
    -d '{
      "payment_intent_id": "pi_000000000000000000000000",
      "amount": 1500,
      "reason": "requested_by_customer"
    }'
  ```
</CodeGroup>

Use a partial `amount` when refunding less than the original payment. Omit `amount` for a full refund when supported by the payment state.

## List refunds

Use refund listing to review refund history or build a dashboard.

<CodeGroup>
  ```ts TypeScript theme={null}
  const refunds = await ionic.refunds.list({ limit: 20 });
  ```

  ```bash curl theme={null}
  curl https://api.ionicfi.com/v1/refunds?limit=20 \
    -H "Authorization: Bearer sk_v1_test_..."
  ```
</CodeGroup>

## Retrieve a refund

<CodeGroup>
  ```ts TypeScript theme={null}
  const refund = await ionic.refunds.retrieve({
    id: "rf_000000000000000000000000",
  });
  ```

  ```bash curl theme={null}
  curl https://api.ionicfi.com/v1/refunds/rf_000000000000000000000000 \
    -H "Authorization: Bearer sk_v1_test_..."
  ```
</CodeGroup>

## Best practices

* Store your own order ID in checkout session `metadata`.
* Keep the mapping from a refund to its support ticket or return
  authorization in your own system, keyed by the refund id; refunds carry a
  `reason` but no free-form metadata.
* Treat refund creation as a money-moving operation and always retry with the same `Idempotency-Key`.
* Check the refund's API status before telling a buyer it is complete.

## Manual capture

By default, payments capture automatically on confirmation. Create the
payment intent with `capture_method: "manual"` to authorize first and move
the money later — useful when you ship before you charge, or need a final
review step.

A confirmed manual-capture intent sits in `requires_capture`. Capture it to
move it to `succeeded`:

<CodeGroup>
  ```ts TypeScript theme={null}
  const captured = await ionic.paymentIntents.capture({
    id: "pi_...",
    "Idempotency-Key": "capture-order-8241",
  });
  ```

  ```bash curl theme={null}
  curl -X POST https://api.ionicfi.com/v1/payment_intents/pi_.../capture \
    -H "Authorization: Bearer sk_v1_test_..." \
    -H "Idempotency-Key: capture-order-8241"
  ```
</CodeGroup>

Capture takes no body and captures the full authorized amount. Authorizations
don't hold forever — card networks expire them after a period set by the
issuer, so capture promptly once you're ready.

## Cancel an uncaptured payment

An intent that hasn't been captured can be canceled instead, releasing the
buyer's authorization hold:

<CodeGroup>
  ```ts TypeScript theme={null}
  const canceled = await ionic.paymentIntents.cancel({
    id: "pi_...",
    reason: "requested_by_customer",
  });
  ```

  ```bash curl theme={null}
  curl -X POST https://api.ionicfi.com/v1/payment_intents/pi_.../cancel \
    -H "Authorization: Bearer sk_v1_test_..." \
    -H "Content-Type: application/json" \
    -d '{"reason": "requested_by_customer"}'
  ```
</CodeGroup>

The body is optional — an absent body cancels with no reason. Canceling is
simpler for the buyer than capture-then-refund: the hold drops off their
statement instead of a charge appearing and reversing.

Before capture, cancel an intent. After capture, refund it.
