Refund and dispute handling
Estimate, create, and track refunds for sessions and invoices.
Last updated
Was this helpful?
Estimate, create, and track refunds for sessions and invoices.
Last updated
Was this helpful?
Was this helpful?
You are helping me add refund handling to my existing CoinCircuit payment integration. I have the CoinCircuit MCP server connected.
**If you have access to the CoinCircuit MCP server, call these tools for the most accurate and detailed schema outputs:**
- Call `search_api` with feature `Refunds` in the CoinCircuit MCP to see all available refund endpoints (estimate, create session refund, create invoice refund, list, get by ID).
- Call `get_endpoint` with method `post` and path `/api/v1/refunds/session/{sessionReference}` for the session refund request/response schema.
- Call `get_endpoint` with method `post` and path `/api/v1/refunds/invoice/{invoiceReference}` for the invoice refund schema.
- Call `get_endpoint` with method `get` and path `/api/v1/refunds/estimate/{reference}` for the refund estimation schema.
- Call `get_schema` with name `RefundCreatedWebhookDto` for the refund.created webhook payload.
- Call `get_schema` with name `RefundSuccessWebhookDto` for the refund.success webhook payload.
- Call `get_schema` with name `RefundFailedWebhookDto` for the refund.failed webhook payload.
- Call `get_schema` with name `RefundResponseDto` for the full refund object shape used in both API responses and webhooks.
Use the live MCP data as your source of truth. The details below are a guide, but if the MCP returns something different, trust the MCP.
## API Basics
- **Base URL:** `https://api.coincircuit.io`
- **Auth:** `x-api-key` header on every request.
## Project Context
I have an application using CoinCircuit for payments and I need to handle customer refund requests for both checkout session payments and invoice payments. CoinCircuit sends crypto refunds to a wallet address you specify.
## What I Need You to Implement
### 1. Estimate refund before processing
Before issuing a refund, show the customer what they'll receive after fees.
**Endpoint:** `GET /api/v1/refunds/estimate/{reference}?entity={type}`
*(Call `get_endpoint` with method `get`, path `/api/v1/refunds/estimate/{reference}` in the CoinCircuit MCP for the full parameter and response details.)*
**Path params:**
- `reference` (string) - the session reference (e.g. `CS_1234567890`) or invoice reference (e.g. `INV_1234567890`)
**Query params:**
- `entity` (required) - `"session"` or `"invoice"`
- `feePaidBy` (optional) - `"merchant"` or `"customer"`
Use this to display a confirmation screen: "Refund amount: X USDT, Network fee: Y USDT, Customer receives: Z USDT".
### 2. Refund a checkout session
**Endpoint:** `POST /api/v1/refunds/session/{sessionReference}`
*(Call `get_endpoint` with method `post`, path `/api/v1/refunds/session/{sessionReference}`, section `request` in the CoinCircuit MCP for the exact request body.)*
**Path params:**
- `sessionReference` (string) - the session reference from the original payment
**Request body:**
- `refundAddress` (string, **required**) - the customer's wallet address to receive the refund
- `reason` (string, optional) - e.g. "Customer requested refund", "Product not delivered"
- `feePaidBy` (string, optional) - `"merchant"` or `"customer"`. Defaults to your dashboard settings. Controls who absorbs the blockchain gas fee.
**Example:**
```json
{
"refundAddress": "TF6yMCJqFcT6wFFutxVmRocKgJFD5imKUT",
"reason": "Customer requested refund - product not as described",
"feePaidBy": "merchant"
}
```
**Response (201):** Returns the refund object:
- `data.id` - refund UUID
- `data.entity` - `"session"`
- `data.reference` - the session reference
- `data.status` - `"pending"`, `"processing"`, `"completed"`, or `"failed"`
- `data.chain` - blockchain used for the refund (matches original payment)
- `data.asset` - crypto asset (e.g. `"USDT"`)
- `data.amount` - crypto amount the customer receives (e.g. `"49.50000000"`)
- `data.fee` - network fee in crypto (e.g. `"0.50000000"`)
- `data.feePaidBy` - `"merchant"` or `"customer"`
- `data.balanceSource` - `"merchantBalance"` or `"pendingBalance"`
- `data.merchantDebitAmount` - fiat amount debited from your balance
- `data.fiatAmount` / `data.fiatCurrency` - fiat value of the refund
- `data.refundAddress` - customer's wallet
- `data.txHash` - blockchain tx hash (populated on completion)
- `data.explorerUrl` - block explorer link (populated on completion)
*(Call `get_schema` with name `RefundResponseDto` in the CoinCircuit MCP for every field in the refund object, including which are required vs optional.)*
### 3. Refund an invoice
**Endpoint:** `POST /api/v1/refunds/invoice/{invoiceReference}`
*(Call `get_endpoint` with method `post`, path `/api/v1/refunds/invoice/{invoiceReference}` in the CoinCircuit MCP for the full schema.)*
Same request body as session refunds: `refundAddress` (required), `reason` (optional), `feePaidBy` (optional). Same response shape.
### 4. Track refund status via webhooks
*(Call `get_schema` with name `RefundSuccessWebhookDto` in the CoinCircuit MCP for the full refund.success webhook payload. Call `get_schema` with name `RefundFailedWebhookDto` for the failure payload.)*
**Refund events** (envelope: `{ event: string, data: { refund: RefundObject } }`):
- `refund.created` - refund initiated, blockchain transaction pending. `data.refund.status` is `"pending"`. *(Call `get_schema` with name `RefundCreatedWebhookDto` for the full payload.)*
- `refund.success` - crypto sent to customer's wallet. `data.refund.txHash` and `data.refund.explorerUrl` are populated. `data.refund.completedAt` has the timestamp.
- `refund.failed` - refund failed (insufficient hot wallet balance, invalid address, etc.). `data.refund.status` is `"failed"`.
**Refund object fields in webhook:**
- `data.refund.id` - refund UUID
- `data.refund.entity` - `"session"` or `"invoice"`
- `data.refund.reference` - original session/invoice reference
- `data.refund.status` - `"pending"`, `"processing"`, `"completed"`, `"failed"`
- `data.refund.amount` - crypto amount customer receives
- `data.refund.fee` - network fee
- `data.refund.feePaidBy` - who paid the fee
- `data.refund.refundAddress` - customer's wallet
- `data.refund.txHash` - blockchain tx hash (on success)
- `data.refund.explorerUrl` - block explorer URL (on success)
- `data.refund.merchantDebitAmount` - fiat amount debited from merchant
- `data.refund.fiatAmount` / `data.refund.fiatCurrency` - fiat value
### 5. Query refund history
*(Call `search_api` with feature `Refunds` in the CoinCircuit MCP to see all listing and detail endpoints.)*
**List all refunds:** `GET /api/v1/refunds` (returns paginated list)
*(Call `get_endpoint` with method `get`, path `/api/v1/refunds` in the CoinCircuit MCP for pagination params and response schema.)*
**Get single refund:** `GET /api/v1/refunds/{id}`
*(Call `get_endpoint` with method `get`, path `/api/v1/refunds/{id}` in the CoinCircuit MCP for the full response.)*
Use these to build a support dashboard showing all refunds with their status, amounts, and transaction details.
### 6. Customer communication flow
Build email/notification triggers based on webhook events:
- On `refund.created`: "Your refund has been initiated. We're sending {amount} {asset} to your wallet."
- On `refund.success`: "Your refund of {amount} {asset} has been sent to {refundAddress}. Track it here: {explorerUrl}"
- On `refund.failed`: "We encountered an issue processing your refund. Our team is looking into it. Reference: {reference}"
## Constraints
- `refundAddress` is **required**. CoinCircuit needs to know where to send the crypto.
- Refunds are async. The API returns immediately with `"pending"` status. Use webhooks for final status.
- A session or invoice can only be refunded **once**.
- You cannot refund more than the original payment amount.
- The refund is sent in crypto (on the same chain/asset as the original payment) regardless of the original fiat denomination.
- Network fees (gas) may be deducted from the refund amount (if `feePaidBy: "customer"`) or from your merchant balance (if `feePaidBy: "merchant"`).
- Use the estimate endpoint first to show the customer what they'll receive.
- Refund statuses: `"pending"` -> `"processing"` -> `"completed"` or `"failed"`.