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

# Crypto Refunds

> **Warning**
>
> **This page is for advanced / cryptocurrency-native companies.** If that's not you, head back to the [Quickstart](/guides/getting-started/quickstart) for the standard flows.

Once a crypto payment has **successfully settled**, you can programmatically issue a refund to your customer’s wallet. Refunds are processed on-chain, and you’ll receive a **transaction hash** that can be shared with the end user as proof of refund.

Before issuing a refund:

* The original payment must be **settled**
* You must have the `paymentId`
* You must provide a valid **destination wallet address**

---

### Step 1: [Initiate a Refund](https://docs.coinflow.cash/api-reference/api-reference/merchant/refund-payment?explorer=true)

Send a `PUT` request to the refund endpoint with the desired refund details.

### Request

```bash
curl --location --request PUT 'https://api-sandbox.coinflow.cash/api/merchant/payments/{paymentId}/refund' \
--header 'Authorization: coinflow_sandbox_11111111111111111111111111111111_11111111111111111111111111111111' \
--header 'Content-Type: application/json' \
--data '{
  "refundReason": "userCancellation",
  "destinationWallet": "123abcd",
  "partialAmount": {
    "cents": 200
  }
}'
```

#### Parameters

| Field                      | Type    | Description                                                                                                                  |
| -------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `refundReason`             | string  | Reason for refund (e.g. `userCancellation`)                                                                                  |
| `destinationWallet`        | string  | Wallet address where funds will be sent                                                                                      |
| `partialAmount`            | object  | Amount to refund in cents (optional; omit for full refund)                                                                   |
| `allowSubMinimumRemainder` | boolean | Optional. Accept a partial refund that leaves a remainder below the refund minimum. See [Refund minimums](#refund-minimums). |

#### Response

```json
"RefundPipeline{paymentId}"
```

This indicates the refund has been initiated and is being processed.

---

### Step 2: [Retrieve Payment Refund Details](https://docs.coinflow.cash/api-reference/api-reference/merchant/get-payment-by-id?explorer=true)

To confirm the refund and retrieve on-chain details, fetch the payment by ID.

#### Request

```bash
curl --location 'https://api-sandbox.coinflow.cash/api/merchant/payments/{paymentId}' \
--header 'Authorization: coinflow_sandbox_11111111111111111111111111111111_11111111111111111111111111111111'
```

---

### Step 3: Inspect Refund Data

Within the response, reference the following fields:

#### Full Example Response

```json
{
  "_id": "69fa33f366fad13675d6c897",
  "paymentId": "51e5ac49-d3ad-412c-afdb-acb1a5b50b3d",
  "totals": {
    "subtotal": { "cents": 300, "currency": "USD" },
    "creditCardFees": { "cents": 2, "currency": "USD" },
    "chargebackProtectionFees": { "cents": 0, "currency": "USD" },
    "gasFees": { "cents": 0, "currency": "USD" },
    "fxFees": { "cents": 0, "currency": "USD" },
    "total": { "cents": 302, "currency": "USD" },
    "merchantPaidCreditCardFees": { "cents": 0, "currency": "USD" },
    "merchantPaidGasFees": { "cents": 0, "currency": "USD" },
    "merchantPaidFxFees": { "cents": 0, "currency": "USD" }
  },
  "customer": "69fa33f366fad13675d6c894",
  "merchant": "68dae987ec61c5361b320e50",
  "webhookInfo": {
    "item": "sword",
    "email": ""
  },
  "chargebackProtectionDecision": "Not Enabled",
  "cryptoInfo": {
    "status": "REFUNDED",
    "sessionId": "0f495375-1796-4d12-a3a2-07cc6563a23d",
    "actualPaymentAmountUSD": "4.01196",
    "blockchainLogoUrl": "https://static.logo.xyz/logos/solana-7228edcd.png",
    "originatingTransactionHash": "5xWjaoesF1SyRaz4vgX2jKmz6whsxWBAWqeKuvj46dPn9idbYTi7PvGzTa2iMUpY4poyK7G4CS6FSr1Zm1e1234",
    "originatingWallet": "9rpv2W6qyShwcwTgZXpiFuC5kFGYpzhYugmpKK5L1234",
    "paymentCurrency": "solana:103/token:111111111111111111111111111",
    "sponsoredTransactionHash": "4JpcdKKjMi1MzM5fayUwncyfaN8CKEJgPzdGmoU2SF8UfKyn4N75HmhoxRmia8WZn7W5dk91T6ttcy9MM73TPKS9",
    "tier": "stables",
    "token": "USDC",
    "tokenAmount": "4.01196",
    "tokenLogoUrl": "https://static.logo.com/logos/usdc-8aaf5df7.png",
    "refundAddress": "91eEM5xHu8tY82tiNkjzJNXGLM7NhyCEVaRhvs9p1234",
    "refundTransactionHash": "2cboz1RP9H9nY5yhcSz3xCBfZhMXrkfWf4rZbKnmGAK42xWyuP6R66iTDc2AwFPSonKDGqycR9ZtpGnmy4qQ1234"
  },
  "settlementType": "USDC",
  "blockchain": "solana",
  "createdAt": "2026-05-05T18:16:19.342Z",
  "updatedAt": "2026-05-05T18:19:44.138Z",
  "refundInfo": {
    "amount": { "cents": 200, "currency": "USD" },
    "refundedAt": "2026-05-05T18:19:30.297Z",
    "count": 1
  }
}
```

---

### Step 4: Share Refund Receipt

Provide the `cryptoInfo.refundTransactionHash` to your customer.

* This is the **on-chain transaction hash**
* It can be used to verify the refund on a blockchain explorer
* Acts as a **proof of refund**

---

---

### Refund minimums

Crypto refunds have a **minimum amount** that depends on the asset and network
being refunded — it's driven mainly by the on-chain network fee to deliver the
refund, so higher-fee networks carry a higher floor. A refund below the minimum
for its asset is rejected. Reach out to your Coinflow integrations contact for the
exact minimum that applies to the assets you support.

#### Partial refunds and stranded remainders

When you issue a [partial refund](/guides/checkout/payment-scenarios/refunds/partial-refunds),
the amount left **un-refunded** (the remainder) must be either **zero** or **at or
above the refund minimum**. If a partial refund would leave a remainder below the
minimum, that remainder could never be refunded afterward — it would fail the same
minimum — so the request is rejected:

```json
{
  "msg": "Refunding this amount leaves $0.50 unrefundable — below the $1.00 minimum, so it can never be refunded later. Refund the full remaining $2.00, or accept this.",
  "code": "refund_strands_remainder"
}
```

| Field       | Value                      |
| ----------- | -------------------------- |
| HTTP status | `409`                      |
| `code`      | `refund_strands_remainder` |

You have three options:

1. **Refund the full remaining amount** instead of a partial amount.
2. **Choose a smaller partial amount** that leaves the remainder at or above the minimum.
3. **Accept the stranded remainder** — knowingly leave it un-refundable — by
   resubmitting the same request with `allowSubMinimumRemainder: true`.

From the **merchant dashboard** this surfaces as a warning with an **"Accept &
refund anyway"** button that performs option 3.

> **Warning**
>
> Refunding below the asset minimum (the refund amount itself, not just the
> remainder) is always rejected and **cannot** be overridden — only the
> sub-minimum *remainder* case above is acceptable via `allowSubMinimumRemainder`.

---

### Notes & Best Practices

* Refunds are **irreversible once submitted on-chain**
* Always validate the `destinationWallet` before initiating
* Use partial refunds (`partialAmount`) when issuing refunds for a portion of the original payment
* Monitor `cryptoInfo.status` to confirm refund completion (`REFUNDED`)