> ## 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>

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`.

## 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.

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, such as `SourceReturn`, `ProviderBalance`, or `ManualRequired`.
</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="dispatchModes" type="array of strings">
  How the refund is dispatched and tracked, such as `SubmitAndPoll`, `PollOnly`, or `Manual`.
</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/corridors/{country}/{fiat}/off-ramp/refund-capabilities \
    --header 'Authorization: Bearer <token>'
  ```

  ```json Response theme={null}
  [
    {
      "pfiName": "Bridge",
      "supportedPaths": ["SourceReturn"],
      "acceptedRequestTypes": ["Address"],
      "destinationModes": ["address_optional"],
      "dispatchModes": ["SubmitAndPoll"],
      "chainsSupported": ["USDC_POLYGON"],
      "estimatedTimingRange": {
        "minSeconds": 3600,
        "maxSeconds": 259200,
        "businessDays": true
      },
      "autoRefundOverrideSupported": false,
      "prerequisites": []
    }
  ]
  ```
</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,
    "path": "SourceReturn",
    "status": "InProgress",
    "dispatchMode": "SubmitAndPoll",
    "destinationSource": "Manual",
    "destination": {
      "type": "Address",
      "address": "0x1234567890abcdef1234567890abcdef12345678"
    },
    "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.

<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. Only plans in `Created` or `Eligible` status can be dispatched manually, and any `refundDestination` override is re-validated against the PFI's refund capability before it is applied.
</Note>

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