---
title: Refunds
description: Refund crypto payments with Coinetic: create a request with POST /v1/refunds, amount and address rules, sending from your own wallet, statuses, refund.sent.
url: https://coinetic.money/docs/refunds/
lang: en
---

# Refunds

A refund sends funds back to the customer: when an order is canceled, overpaid, or underpaid after expiration. You create one as a request via the API or in the dashboard.

## Who sends the money

It depends on the request's `executor` field:

- **`merchant`: the payment went to your wallet** (the usual case). Coinetic doesn't hold your funds, so you send the transfer to the customer yourself and record the transaction hash.
- **`platform`: the payment went to a platform wallet.** You only request the refund; a Coinetic operator verifies the address, approves and sends it.

## Rules

- **Only in the payment asset.** If the customer paid USDT on TRON, they get USDT on TRON. There's no conversion.
- **No more than was paid**: see the invoice's `refundable_amount`. Partial refunds are allowed, up to 20 open requests per invoice.
- **The address must be on the payment network.** It's validated against the network's format (EVM uses the EIP-55 checksum). Coinetic's own addresses aren't accepted.
- **A paid invoice stays paid.** The amount refunded is tracked in `refunded_amount`.
- **A refund permanently closes an unpaid invoice**, even a partial one: the invoice becomes `cancelled`, and top-ups will no longer pay for the order. Check the invoice status before submitting a request.

## Create a request

`POST /v1/refunds`

```http
POST /v1/refunds
Idempotency-Key: refund-ORDER-1001-1
Content-Type: application/json

{"invoice_id":"inv_x7k2…","amount_minor":"10500000","address":"TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE","reason":"Order ORDER-1001 canceled"}
```

| Field | Required | Value |
| --- | --- | --- |
| `invoice_id` | yes | An invoice from your store |
| `amount_minor` | yes | **Smallest units** as a string (`"10500000"` = 10.5 USDT at 6 decimals), or `"full"` for the entire available amount |
| `address` | yes | The customer's address on the payment network |
| `reason` | no | Reason, up to 500 characters |

## Refunding from your wallet

- Send the request's `amount` to the customer from your own wallet, in the payment asset and network (you pay the network fee).
- Record the transaction hash:

```http
POST /v1/refunds/{id}/executed
Idempotency-Key: refund-exec-ORDER-1001-1
Content-Type: application/json

{"tx_hash":"0x5c50…","ack_status_change":false}
```

The request moves to `reported`. On EVM networks (Ethereum, BSC, Polygon, Base, Arbitrum), Coinetic finds the confirmed transaction on-chain, moves the request to `sent` and sends a `refund.sent` webhook. Bitcoin, Litecoin, TRON and Solana don't have automatic verification yet, so the request stays `reported`.

Changed your mind? Call `POST /v1/refunds/{id}/cancel` with a reason, and the amount becomes refundable again.

## Statuses

| Status | Meaning |
| --- | --- |
| `requested` | Request accepted |
| `approved` | Approved by an operator (`executor: platform` only) |
| `reported` | You've reported the hash; awaiting on-chain confirmation |
| `sent` | Funds sent; `tx_hash` is the on-chain transaction |
| `rejected` | Rejected or canceled; the reason is in `reject_reason` |

Rejections aren't sent as webhooks, so poll open requests on a schedule: `GET /v1/refunds?status=requested`.

## In the sandbox

`POST /v1/refunds/{id}/simulate` runs the whole flow at once: the request becomes `sent` with a test `tx_hash`, and `refund.sent` fires. This route doesn't exist in production.
