# Simulate Refund

> Transition a sandbox invoice refund to completed, failed, or cancelled and trigger the corresponding signed webhook.

`POST /v1/sandbox/refunds/:withdrawal_id/simulate`

:::endpoint
POST /v1/sandbox/refunds/:withdrawal_id/simulate
:::

**API key:** `Payment` (sandbox)

Sandbox only. Transitions an existing sandbox refund (`is_test: true`) to the requested terminal status (`completed`, `failed`, or `cancelled`), updates the sandbox ledger, and emits the corresponding `withdrawal.completed`, `withdrawal.failed`, or `withdrawal.cancelled` webhook with the full refund payload.

## Request body

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `status` | string | Yes | Target terminal status: `"completed"`, `"failed"`, or `"cancelled"` |

```json
{
  "status": "completed"
}
```

## Response (200 OK)

```json
{
  "withdrawal_id": "wdr_6Qm2fK8xR4vN7cT1bY9dL5",
  "invoice_id": "inv_7K2M9P4Q8R1X5Z3A0bC2dE",
  "mode": "full",
  "external_order_id": "refund-1042-full",
  "status": "completed",
  "is_final": true,
  "is_test": false,
  "amount": "100.00",
  "fee": "1.00",
  "currency": "USDT",
  "network": "TRC20",
  "destination_address": "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
  "first_sender": "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
  "tx_hash": "9f3b8c2a1d0e4f6789abc123def4567890abc123def4567890abc123def45678",
  "explorer_url": "https://tronscan.org/#/transaction/9f3b8c2a1d0e4f6789abc123def4567890abc123def4567890abc123def45678",
  "created_at": 1767225600,
  "completed_at": 1767225932,
  "type": "refund",
  "project_id": "prj_3F9xL2mN8pQ4rT6vW1yZ5b"
}
```

## Rules

:::dash-list
- Create the sandbox refund first with [`POST /v1/refunds`](/docs/refunds/create.md) using a sandbox Payment key (`pk_test_`), then pass its `withdrawal_id` to this endpoint.
- Calling `simulate` on a refund that is already in the requested terminal status is idempotent and returns `200 OK` without emitting a duplicate webhook.
- Calling `simulate` with a different terminal status after the refund is already final returns `409 invalid_withdrawal_state`.
- Simulating `"completed"` finalizes the reserved balance debit and assigns a synthetic `tx_hash`. Simulating `"failed"` or `"cancelled"` releases the reserved `amount + fee` back to your sandbox balance, allowing you to create a new refund for the invoice.
:::

## Errors

| Code | HTTP | When |
|------|------|------|
| `payment_key_required` | 403 | The request was signed with a Payout (`rk_`) key |
| `refunds_not_enabled` | 503 | Invoice refunds are disabled in this environment |
| `withdrawal_not_found` | 404 | The ID does not exist, is not a sandbox refund, or is outside the key's project scope |
| `invalid_withdrawal_state` | 409 | The refund is already in a different terminal state |
| `field_invalid_enum` | 400 | `status` is not `"completed"`, `"failed"`, or `"cancelled"` |

See [Testing](/docs/testing.md) for the complete sandbox workflow and [Error Codes](/docs/errors/codes.md) for the full catalogue.
