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

# Refunds

A refund returns funds to the originator when a transaction cannot be completed after funds have already moved. This can happen for several reasons — the receiving bank returns the payment, a compliance review reverses it, the transaction expires or fails after funds left the source, or an explicit refund is requested. When a refund completes, the original transaction reaches the `Refunded` status.

<Note>
  If you're just getting started, please check out our [quick start guide](/docs/quick-start-guide).
</Note>

Like every operation on Borderless, refunds are **asynchronous**. You can track a refund either via webhooks or by polling the transaction. For background on the transaction lifecycle and the `Refunded` status, see [Transactions](/docs/transactions).

## How refunds work

When a transaction is created, Borderless builds a **refund plan** for it. The plan records how a refund would be handled if it becomes necessary — where the funds should be returned (the refund destination) and whether the refund should be triggered automatically.

If the transaction later fails or is reversed:

* When the plan has **auto-refund enabled** (the default), Borderless dispatches the refund automatically.
* When auto-refund is **disabled**, the plan stays eligible but waits until the refund is dispatched manually through the [refund endpoint](#executing-a-refund-plan).

<Note>
  Whether a refund is supported, and which return paths are available, depends on the partner financial institution (PFI) handling the transaction.
</Note>

## Refund destinations

A refund destination describes where returned funds should go. It is configured with the following fields:

<ResponseField name="type" type="string enum" required>
  How the refund should be handled. One of:

  * `Address` — return funds to a specific crypto address.
  * `Source` — return funds to the original on-chain sender.
  * `ProviderBalance` — leave the funds in your account balance at the PFI.
</ResponseField>

<ResponseField name="address" type="string">
  The crypto address that should receive the refund. Required when `type` is `Address`.
</ResponseField>

<ResponseField name="autoRefund" type="boolean">
  Whether Borderless should automatically trigger the refund on failure. Defaults to `true`. Setting it to `false` records the plan as eligible but keeps it waiting until the refund is dispatched manually.
</ResponseField>

These are the fields for a **per-transaction** destination. The organization-wide default takes a slightly different shape — see [Organization default refund destination](#organization-default-refund-destination).

You can attach a `refundDestination` when creating a withdrawal. The examples below show each destination type:

<CodeGroup>
  ```json Address theme={null}
  {
    "paymentInstructionId": "cm2c4x3cc000019stwv7um4zl",
    "paymentPurpose": "delivery fees",
    "refundDestination": {
      "type": "Address",
      "address": "0x1234567890abcdef1234567890abcdef12345678",
      "autoRefund": true
    }
  }
  ```

  ```json Source theme={null}
  {
    "paymentInstructionId": "cm2c4x3cc000019stwv7um4zl",
    "paymentPurpose": "delivery fees",
    "refundDestination": {
      "type": "Source",
      "autoRefund": true
    }
  }
  ```

  ```json ProviderBalance theme={null}
  {
    "paymentInstructionId": "cm2c4x3cc000019stwv7um4zl",
    "paymentPurpose": "delivery fees",
    "refundDestination": {
      "type": "ProviderBalance",
      "autoRefund": false
    }
  }
  ```
</CodeGroup>

## Where the destination comes from

The refund destination is resolved from three layers. Each layer overrides the one before it, so the most specific configuration always wins:

```mermaid theme={null}
flowchart TD
    A([Refund destination needed]) --> B{Execute Plan override?<br/>POST /transactions/:id/refund}
    B -- yes --> M[/"Use override<br/>destinationSource: Manual"/]
    B -- no --> C{Withdrawal refundDestination<br/>set at creation?}
    C -- yes --> P[/"Use per-transaction value<br/>destinationSource: PerTransaction"/]
    C -- no --> D{Organization default<br/>set?}
    D -- yes --> O[/"Use org default<br/>destinationSource: OrganizationDefault"/]
    D -- no --> N[/"No destination resolved<br/>destinationSource: None"/]

    classDef win fill:#199eff,stroke:#0b6fb0,color:#fff;
    class M,P,O win;
```

1. **Organization default** — an org-wide fallback applied to every transaction. Resolves to `destinationSource: OrganizationDefault`.
2. **Withdrawal** — the `refundDestination` you set on the create request. Overrides the organization default and resolves to `destinationSource: PerTransaction`.
3. **Execute Plan** — a `refundDestination` override passed to the refund endpoint, applied just before the refund is dispatched. Takes precedence over everything and resolves to `destinationSource: Manual`.

Two further `destinationSource` values can appear when the PFI, rather than your configuration, determines where the money goes: `OriginalSender` when the funds are reversed to the on-chain sender, and `ProviderLocked` when the PFI returns them to a destination you cannot influence. `None` means no destination could be resolved.

## Organization default refund destination

The organization default is the fallback applied to any transaction that has no per-transaction destination. Reading it is available to any authenticated caller; creating, replacing, re-confirming, and removing it require the **Owner** role.

| Method | Endpoint | Description |
| - | - | - |
| `GET` | `/v1/refunds/config` | Fetch the current default. Returns `null` when none is configured. |
| `PUT` | `/v1/refunds/config` | Create or replace the default. Resets the verification timestamp to now. |
| `POST` | `/v1/refunds/config/reconfirm` | Reset the verification timestamp without changing the destination. |
| `DELETE` | `/v1/refunds/config` | Remove the default. Responds with `204 No Content`. |

<Warning>
  Unlike a per-transaction destination, which carries a single `address`, the organization default takes an **`addresses` array with one entry per network**. At refund time the address matching the transaction's network is used. Networks with no entry have no default and fall back to per-transaction destinations.
</Warning>

<ResponseField name="destination.type" type="string enum" required>
  One of `Address`, `Source`, or `ProviderBalance`, as described above.
</ResponseField>

<ResponseField name="destination.addresses" type="array of objects">
  Required when `type` is `Address`. Each entry is a `network` and the `address` that should receive refunds on it. At least one entry.
</ResponseField>

<ResponseField name="destination.autoRefund" type="boolean">
  Whether Borderless automatically triggers the refund on failure. Defaults to `true`.
</ResponseField>

<ResponseField name="destination.autoFiatReturn" type="boolean">
  Whether Borderless automatically returns a failed fiat payout as crypto, by re-converting the fiat held at the PFI and paying out to the refund destination. Defaults to `false`; when disabled the plan is marked for manual handling instead. This field has no per-transaction equivalent.
</ResponseField>

<CodeGroup>
  ```bash Request theme={null}
  curl --request PUT \
    --url https://api.borderless.xyz/v1/refunds/config \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --data '{
      "destination": {
        "type": "Address",
        "addresses": [
          {
            "network": "ETHEREUM",
            "address": "0x1234567890abcdef1234567890abcdef12345678"
          },
          {
            "network": "POLYGON",
            "address": "0xabcdef1234567890abcdef1234567890abcdef12"
          }
        ],
        "autoRefund": true,
        "autoFiatReturn": false
      }
    }'
  ```

  ```json Response theme={null}
  {
    "destination": {
      "type": "Address",
      "addresses": [
        {
          "network": "ETHEREUM",
          "address": "0x1234567890abcdef1234567890abcdef12345678"
        },
        {
          "network": "POLYGON",
          "address": "0xabcdef1234567890abcdef1234567890abcdef12"
        }
      ],
      "autoRefund": true,
      "autoFiatReturn": false
    },
    "verifiedAt": "2026-08-26T10:15:00.000Z",
    "isStaleWarning": false,
    "isStaleReject": false
  }
  ```
</CodeGroup>

### Keeping the default confirmed

The default carries a `verifiedAt` timestamp and two staleness flags:

<ResponseField name="isStaleWarning" type="boolean">
  True once the default is older than **30 days**. Refunds still run — treat it as a prompt to re-confirm.
</ResponseField>

<ResponseField name="isStaleReject" type="boolean">
  True once the default is older than **90 days**. Automatic refunds to the default are **paused** until it is re-confirmed.
</ResponseField>

Call `POST /v1/refunds/config/reconfirm` to reset `verifiedAt` to now and clear both flags without changing the destination.

## Refund capabilities

Before creating a transaction, you can check which refund behavior is supported for an off-ramp corridor. The capabilities endpoint reports, per partner financial institution (PFI), the refund paths, accepted destination types, supported chains, and estimated timing for a given country and fiat currency.

Both query parameters are required:

<ParamField query="country" type="string enum" required>
  Destination country of the withdrawal corridor, such as `MX`.
</ParamField>

<ParamField query="fiat" type="string enum" required>
  Destination fiat currency of the withdrawal corridor, such as `MXN`.
</ParamField>

The response is an **array** with one entry per PFI that currently serves the corridor. Each entry contains:

<ResponseField name="pfiName" type="string enum">
  The partner financial institution the capabilities apply to.
</ResponseField>

<ResponseField name="supportedPaths" type="array of strings">
  The refund paths the PFI supports for this corridor. One or more of `PfiAutoReversal`, `BalancePayout`, `ReturnToSender`, `PfiAutoReturn`, `ManualRequired`, or `Unsupported`.
</ResponseField>

<ResponseField name="acceptedRequestTypes" type="array of strings">
  The refund destination `type` values you may specify on a transaction. Either `["Address"]` or empty.
</ResponseField>

<ResponseField name="destinationModes" type="array of strings">
  How the refund destination is determined: `none`, `address_optional`, `address_required`, or `provider_locked`.
</ResponseField>

<ResponseField name="chainsSupported" type="array of strings">
  The assets/chains available for refunds on this corridor, such as `USDC_POLYGON`.
</ResponseField>

<ResponseField name="estimatedTimingRange" type="object">
  The expected refund time as `minSeconds`, `maxSeconds`, and a `businessDays` flag indicating whether the range is measured in business days.
</ResponseField>

<ResponseField name="autoRefundOverrideSupported" type="boolean">
  Whether the auto-refund preference can be overridden for this corridor. Currently always `false`.
</ResponseField>

<ResponseField name="prerequisites" type="array of strings">
  Any prerequisites that must be met before a refund can be processed.
</ResponseField>

<CodeGroup>
  ```bash Request theme={null}
  curl --request GET \
    --url 'https://api.borderless.xyz/v1/refunds/capabilities?country=MX&fiat=MXN' \
    --header 'Authorization: Bearer <token>'
  ```

  ```json Response theme={null}
  [
    {
      "pfiName": "Bridge",
      "supportedPaths": ["PfiAutoReversal"],
      "acceptedRequestTypes": ["Address"],
      "destinationModes": ["address_optional"],
      "chainsSupported": ["USDC_POLYGON"],
      "estimatedTimingRange": {
        "minSeconds": 3600,
        "maxSeconds": 172800,
        "businessDays": false
      },
      "autoRefundOverrideSupported": false,
      "prerequisites": [
        "A `Source`-typed refund destination sends no explicit instruction to Bridge; it relies on Bridge's default reversal to the original sender. Only `Address`-typed destinations are forwarded as an explicit refund address."
      ]
    }
  ]
  ```
</CodeGroup>

<Note>
  Capabilities are scoped to the PFIs currently enabled for your organization. An empty array means no enabled PFI serves that corridor.
</Note>

## Executing a refund plan

When auto-refund is disabled, or when you need to override the stored destination, you can dispatch the refund manually:

<CodeGroup>
  ```bash Request theme={null}
  curl --request POST \
    --url https://api.borderless.xyz/v1/transactions/{id}/refund \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --data '{
      "refundDestination": {
        "type": "Address",
        "address": "0x1234567890abcdef1234567890abcdef12345678"
      }
    }'
  ```

  ```json Response theme={null}
  {
    "supported": true,
    "requiresManualAction": false,
    "dispatchable": false,
    "path": "PfiAutoReversal",
    "status": "InProgress",
    "destinationSource": "Manual",
    "destination": {
      "type": "Address",
      "address": "0x1234567890abcdef1234567890abcdef12345678",
      "autoRefund": true
    },
    "reason": null,
    "completedAt": null,
    "failedAt": null
  }
  ```
</CodeGroup>

The request body is optional — call the endpoint with an empty body to dispatch using the destination already stored on the plan. The endpoint responds with `202 Accepted` and returns the current refund resolution.

The response includes a `dispatchable` flag telling you whether the plan can be dispatched through this endpoint right now, so you can check it before calling:

<ResponseField name="dispatchable" type="boolean">
  Whether the refund plan can be dispatched now. True for plans in `Created` or `Eligible` status, and for `ManualRequired` plans parked after a failed fiat payout.
</ResponseField>

<ResponseField name="reason" type="object">
  Present when a refund attempt has failed. Contains a human-readable `message` and a `details` array of provider-level error details. `null` otherwise.
</ResponseField>

<Note>
  Dispatching a refund through this endpoint bypasses the `autoRefund: false` preference, so it is the way to trigger refunds for transactions created with auto-refund disabled. Any `refundDestination` override is re-validated against the PFI's refund capability before it is applied.
</Note>

<Warning>
  Plans in `ManualRequired` status can also be dispatched when they were parked after a failed fiat payout. Dispatching such a plan records your consent to return the funds **in crypto, converted from the fiat held at the PFI at the current rate** — so the returned amount may differ from the original.
</Warning>

<Info>
  If you have any questions, please don't hesitate to reach out to us via [email](mailto:support@borderless.xyz) or slack.
</Info>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.