> Documentation index: [Saleor](/llms.txt) · [This section](/developer/llms.txt)
> Source: https://docs.saleor.io/developer/payments/refunds

# Refunds

There are two ways to process refunds:

-   Manual refund: This method involves directly refunding the payment through the payment app.
-   Refund based on `OrderGrantedRefund`: This method triggers the refund request based on the details stored in `OrderGrantedRefund`.

note

You may have seen we start referencing `Page` as a `Model`. While in the API we still use `Page` name for backwards compatibility, we will be renaming it to `Model` in the future.

<a id="manual-refund"></a>

## Manual refund

The manual refund can be triggered by calling `transactionRequestAction`. If the refund is successful, the `transaction.chargedAmount` will be reduced. The order's `authorizeStatus`, `chargeStatus`, and `totalBalance` will also be recalculated based on the new values of `chargedAmount` and `refundedAmount`. This method is useful when handling overcharged orders.

The below example shows how to trigger the manual refund:

**Mutation**

```graphql
mutation TransactionRequestAction($id: ID, $actionType: TransactionActionEnum!, $amount: PositiveDecimal) {
  transactionRequestAction(id: $id, actionType: $actionType, amount: $amount) {
    transaction {
      events {
        id
        message
        pspReference
        amount {
          amount
        }
        type
      }
    }
  }
}
```

**Variables**

```json
{
  "id": "VHJhbnNhY3Rpb25JdGVtOjljY2NkYTYyLTllMjktNGE0OC05NzIyLWRlYzAwOTRmZmY5Yg==",
  "actionType": "REFUND",
  "amount": "10",
  "refundReason": "Customer requested a refund",
  "refundReasonReference": "UGFnZTox"  # ID of a Page/Model representing the reason for the refund
}
```

The mutation accepts below arguments:

-   `id` - ID of the transaction which will be used to trigger the refund action.
-   `actionType` - The type of action to be performed on the requested `TransactionItem`. For a refund action, use `REFUND`.
-   `amount` - The amount of the action. The amount is rounded based on the given currency precision. If not provided Saleor will use `TransactionItem.chargedAmount`.
-   `refundReason` - Optional plain text reason for the refund. Will be populated to the `TransactionEvent.message` field.
-   `refundReasonReference` - The ID of a `Page` representing the reason for the refund. This is useful for tracking and reporting purposes, like aggregating refunds by specific type. See [configuration](#refund-reasons-configuration) to learn more.

<a id="granted-refunds"></a>

## Granted refunds

A granted refund refers to the process where a customer receives their money back after a refund request has been approved. Once a refund is granted, the customer is reimbursed for the purchase.

When processing a refund based on `OrderGrantedRefund`, there are two steps involved. Firstly, an order must be granted a refund, which defines what should be refunded. This step requires the `MANAGE_ORDERS` permission. Secondly, a refund needs to be requested based on the created `OrderGrantedRefund`. This step requires the `HANDLE_PAYMENTS` permission.

A granted refund contains all the details related to the refund, such as the list of refunded lines, the amount, and the included shipping costs. This is useful when handling refunds based on the products returned by the customer.

The current status of the `OrderGrantedRefund` is represented by the `status` field. The possible statuses are:

-   `NONE`: No refund request has been triggered for the granted refund.
-   `PENDING`: The refund request has been triggered, but the payment app has not provided the final result.
-   `SUCCESS`: The refund has been successfully processed.
-   `FAILURE`: The last refund request failed.

The `status` is calculated based on the latest refund `TransactionEvent`s assigned to the `OrderGrantedRefund`. The events can be accessed via the `OrderGrantedRefund.transactionEvents` field. The assigned `TransactionItem` can be accessed via the `OrderGrantedRefund.transaction` field.

The `OrderGrantedRefund` has an impact on the `authorizeStatus`, `chargeStatus`, and `totalBalance`, as it reduces the total value used to calculate the `totalBalance`.

note

The maximum `amount` of `OrderGrantedRefund` is the `order.total`.

For example:

-   if an order has a total of 100 USD and a single `TransactionItem` with a `chargedAmount` of 100 USD, the `totalBalance` would be 0, as `chargedAmount` - `order.total` results in 0(1).
-   Adding a granted refund with an amount of 10 USD would result in a `totalBalance` of 10, as `chargedAmount` - (`order.total` - `grantedRefund.amount`) gives 10. The `chargeStatus` will be `OVERCHARGED`(2).
-   If a refund request is made based on the defined granted refund and is successfully processed by the payment app, it will reduce the `chargedAmount`. The `totalBalance` will be 0, as `chargedAmount` (90USD) - (`order.total` - `grantedRefund.amount`) gives 0. The `chargeStatus` will be changed to `FULL`(3).

| Step Nr | total | totalBalance | authorizeStatus | chargeStatus | tr.chargedAmount | orderGrantedRefund.amount |
| --- | --- | --- | --- | --- | --- | --- |
| 1 | 100 | 0 | FULL | FULL | 100 | 0 |
| 2 | 100 | 10 | FULL | OVERCHARGED | 100 | 10 |
| 3 | 100 | 0 | FULL | FULL | 90 | 10 |

The following example shows how to create the granted refund with the assigned `TransactionItem` for the order.

**Mutation**

```graphql
mutation OrderGrantRefundCreate($id: ID!, $input: OrderGrantRefundCreateInput!) {
  orderGrantRefundCreate(id: $id, input: $input) {
    grantedRefund {
      id
      reason
      reasonReference {
        id
        title
      }
      lines {
        id
        reason
        reasonReference {
          id
          title
        }
        orderLine {
          id
        }
        quantity
      }
    }
    order {
      id
    }
    errors {
      field
      code
      message
      lines {
        lineId
        field
        message
        code
      }
    }
  }
}
```

**Variables**

```json
{
  "id": "T3JkZXI6NWZlOTE5NzItYjg3OC00Y2QyLTkyN2UtZTQwZDJjZDRjMmEz",
  "input": {
    "transactionId": "VHJhbnNhY3Rpb25JdGVtOmUzOTVjNzdmLWFmNjQtNDRmZC05NmRiLThkZmNkMDYwNmZmOA==",
    "lines": [
      {
        "id": "T3JkZXJMaW5lOjdlNzg5NzY0LTUyZWMtNDU3Mi05NWNkLTM5ZjQ0OTJmZDk4ZA==",
        "quantity": 5,
        "reason": "Line reason",
        "reasonReference": "UGFnZToy"
      },
      {
        "id": "T3JkZXJMaW5lOjdlNzg5NzY0LTUyZWMtNDU3Mi05NWNkLTM5ZjQ0OTJmZDk4Z1==",
        "quantity": 5
      }
    ],
    "grantRefundForShipping": true,
    "amount": 10,
    "reason": "Returned by customer",
    "reasonReference": "UGFnZTox"
  }
}
```

The mutation accepts below arguments:

-   `id` - ID of the `Order` to which granted refund should be assigned.
-   `input`:
    -   `lines` - List of lines related to the planned refund action:
        -   `id` - ID of the `OrderLine`.
        -   `quantity` - The quantity of the specific lines planned to refund.
        -   `reason` - Reason of the refund related to the specific line.
        -   `reasonReference` - Optional. ID of the `Page` representing the reason for the refund **for this specific line**. When provided, the Page must match the `PageType` set in refund settings. See [configuration](#refund-reasons-configuration) to learn more. Added in Saleor 3.23.
    -   `grantRefundForShipping` - Determines if the shipping costs will be also included in the refund.
    -   `amount` - Amount of the granted refund. If not provided, the amount will be calculated automatically based on provided `lines` and `grantRefundForShipping`.
    -   `reason` - Order-level reason text for the granted refund.
    -   `reasonReference` - Order-level `Page` ID representing the reason for the refund. Required for staff when `refundReasonReferenceType` is configured. See [configuration](#refund-reasons-configuration) to learn more.
    -   `transactionId` - ID of `TransactionItem` that will be used to process the refund. If `amount` is provided in the input, the `transaction.chargedAmount` needs to be equal to or greater than the provided `amount`. If `amount` is not provided in the input and calculated automatically by Saleor, the `min(calculatedAmount, transaction.chargedAmount)` will be used. This field was added in Saleor 3.20, and it will be a mandatory input field starting from Saleor 3.21.

The order-level `reasonReference` is required for staff users when the `refundReasonReferenceType` is configured, and always optional for apps. Per-line `reasonReference` is always optional for both staff and apps — lines without it are stored with `null`. There is no inheritance from order-level to line-level. When a per-line `reasonReference` is provided, it must match the configured `PageType`.

The below example shows how to update the existing granted refund

**Mutation**

```graphql
mutation OrderGrantRefundUpdate($id: ID!, $input: OrderGrantRefundUpdateInput!) {
  orderGrantRefundUpdate(id: $id, input: $input) {
    grantedRefund {
      id
      reason
      reasonReference {
        id
        title
      }
      lines {
        id
        reason
        reasonReference {
          id
          title
        }
        orderLine {
          id
        }
        quantity
      }
    }
    errors {
      addLines {
        field
        message
        code
        lineId
      }
      field
      code
    }
  }
}
```

**Variables**

```json
{
  "id": "T3JkZXJHcmFudGVkUmVmdW5kOjE=",
  "input": {
    "addLines": [
      {
        "id": "T3JkZXJMaW5lOjgzYmZmZWI3LTVkNjEtNDMzZS1iOGFkLTFhMTE1NWI2ZTgwNg==",
        "quantity": 3,
        "reason": "Damaged item",
        "reasonReference": "UGFnZToy"
      }
    ],
    "removeLines": [],
    "transactionId": "VHJhbnNhY3Rpb25JdGVtOmUzOTVjNzdmLWFmNjQtNDRmZC05NmRiLThkZmNkMDYwNmZmOA==",
    "amount": 10,
    "grantRefundForShipping": false,
    "reason": "New reason",
    "reasonReference": "xXyzaXdaxbX" # ID of a Page
  }
}
```

The mutation accepts below arguments:

-   `id` - ID of the `OrderGrantedRefund` which should be updated
-   `input`:
    -   `addLines` - Lines that should be added to `OrderGrantedRefund`:
        -   `id` - ID of the `OrderLine`.
        -   `quantity` - The quantity of the specific lines planned to refund.
        -   `reason` - Reason for the planned refund related to the specific line.
        -   `reasonReference` - Optional. ID of the `Page` representing the reason for the refund **for this specific line**. When provided, must match the configured `PageType`. See [configuration](#refund-reasons-configuration) to learn more. Added in Saleor 3.23.
    -   `removeLines` - List of `OrderGrantedRefundLine`'s IDs that should be removed from `OrderGrantedRefund`.
    -   `grantRefundForShipping` - Determines if the shipping costs will be also included in the refund.
    -   `amount` - Amount of the granted refund. If not provided, the amount will be calculated automatically based on provided `lines` and `grantRefundForShipping`.
    -   `reason` - Order-level reason text for the granted refund.
    -   `reasonReference` - Order-level `Page` ID representing the reason for the refund. Required for staff when `refundReasonReferenceType` is configured. See [configuration](#refund-reasons-configuration) to learn more.
    -   `transactionId` - ID of `TransactionItem` that will be used to process the refund. If `amount` is provided in the input, the `transaction.chargedAmount` needs to be equal to or greater than the provided `amount`. If `amount` is not provided in the input and calculated automatically by Saleor, the `min(calculatedAmount, transaction.chargedAmount)` will be used. This field was added in Saleor 3.20, and it will be a mandatory input field starting from Saleor 3.21.

info

When `OrderGrantedRefund.status` is `SUCCESS` or `PENDING`, only `reason` can be updated.

Below example shows how to trigger the refund based on `OrderGrantedRefund`:

**Mutation**

```graphql
mutation TransactionRequestRefundForGrantedRefund($grantedRefundId: ID!) {
  transactionRequestRefundForGrantedRefund(grantedRefundId: $grantedRefundId) {
    transaction {
      id
    }
    errors {
      field
      message
      code
    }
  }
}
```

**Variables**

```json
{
  "grantedRefundId": "T3JkZXJHcmFudGVkUmVmdW5kOjE="
}
```

To request a refund, you need to provide the `grantedRefundId` field. The mandatory refund details are stored as `OrderGrantedRefund`, so the refund request will be created based on this data. The `OrderGrantedRefund.status` field will be updated based on the result of the refund action. The `TransactionEvent`s related to the requested action will be accessible via `OrderGrantedRefund.transactionEvents` field.

<a id="total-remaining-grant-calculations"></a>

### Total remaining grant calculations

The amount specified in the `OrderGrantedRefund` determines the portion of the `order.total` that should be refunded to the customer. The `order.totalRemainingGrant` specifies the amount that still needs to be refunded to achieve a state where the total charged amount equals the `order.total` minus the granted refund amount.

The `order.totalRemainingGrant` amount is calculated based on the following formula:

```python
totalRemainingGrant = totalGrantedRefund - alreadyGrantedRefund
```

where

-   `alreadyGrantedRefund` is the difference between already refunded and overcharged amounts
    
    ```python
    alreadyGrantedRefund = max((totalRefunded - overchargedAmount), 0)
    ```
    
-   `totalGrantedRefund` is a sum of all `amount` from all `OrderGrantedRefund`. The maximum value is `order.total`, when the value is above, it's replaced with the `order.total`.
    
    ```python
    totalGrantedRefund = max(sum(orderTotalGranted.amount), order.total)
    ```
    
-   `totalRefunded` is a sum of `amount` from all transactions with refunded or pending refunds amount
    
    ```python
    totalRefunded = sum(amountRefunded + amountRefundPending)
    ```
    
-   `overchargedAmount` defines the `amount` that has been charged over the `order.total` value. It's calculated as a sum of all processed amounts (excluding cancel amounts) minus the `order.total`.
    
    ```python
    processedAmount = sum(amountCharged + amountRefunded + amountAuthorized + amountChargePending + amountRefundPending + amountAuthorizePending)
    
    overchargedAmount = processedAmount - order.total
    ```
    

Below is an example order with multiple transactions and `totalRemainingGrant` at each stage:

1.  An order has a total of 100 USD and two `TransactionItem`s: one with a `chargedAmount` of 100 USD and the second with `chargedAmount` of 60 USD. The `totalBalance` would be 60, as `chargedAmount` - `order.total` results in 60, and we do not have any granted refunds. The `chargeStatus` is `OVERCHARGED`.
    
2.  Adding a granted refund of 10 USD increases the `totalBalance` to 70, as it widens the difference between the total charged amount and the new expected order total. Here's the calculation:
    
    ```python
    totalBalance = totalCharged - (order.total - totalGrantedRefunds) = 160 - (100 - 10) = 70
    ```
    
    The `totalRemainingGrant` becomes 10, as the `totalRefundedAmount` is 0.
    
3.  The second transaction is refunded for an amount of 50 USD. After this operation, the `totalBalance` is 20, and the order remains `OVERCHARGED`. The `totalChargedAmount` decreases, and the `totalRefundedAmount` increases. The `totalRemainingGrant` remains 10 because the `overchargedAmount` still exceeds the original order.total.
    
    ```python
    overchargedAmount = totalChargedAmount + totalRefundedAmount - order.total = 110 + 50 - 100 = 60
    
    totalRefunded = max(totalGrantedRefund - (totalRefunded - overchargedAmount), 0) = max(10 - max(50 - 60, 0)) = 10 - 0 = 10
    ```
    
4.  In the next step the first transaction was refunded for 15 USD. This brings the total below the `order.total` value, reducing the `totalRemainingGrant`.
    
5.  Finally, the last refund of 5 USD is processed, resulting in a `totalBalance` of 0. The `chargeStatus` is `FULL`, and the `totalRemainingGrant` is 0.
    

| Step Nr | total | totalBalance | authorizeStatus | chargeStatus | totalChargedAmount | totalRefundedAmount | orderGrantedRefund.amount | totalRemainingGrant.amount |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 1 | 100 | 60 | FULL | OVERCHARGED | 160 | 0 | 0 | 0 |
| 2 | 100 | 70 | FULL | OVERCHARGED | 160 | 0 | 10 | 10 |
| 3 | 100 | 20 | FULL | OVERCHARGED | 110 | 50 | 10 | 10 |
| 4 | 100 | 5 | FULL | OVERCHARGED | 95 | 65 | 10 | 5 |
| 5 | 100 | 0 | FULL | FULL | 90 | 70 | 10 | 0 |

> **Note:**
> 
> -   `totalChargedAmount` - is the sum of charged amounts from all transactions
> -   `totalRefundedAmount` - is the sum of all refunded or pending refunds amount from all transactions

info

**In case of calculating total remaining grant:**

-   Pending amounts are treated as they were processed, so are included in all processed amounts. It also means that `totalChargedAmount` includes `amountChargePending`, and `totalRefundedAmount` includes `amountRefundPending`.
-   Authorized amounts, including `amountAuthorized` and `amountAuthorizePending`, are also considered processed. These amounts will contribute to the overall processed totals and impact the `overchargedAmount`.

<a id="return-and-replace-reasons"></a>

## Return and replace reasons

note

Available from 3.22

The [`orderFulfillmentReturnProducts`](/api-reference/orders/mutations/order-fulfillment-return-products.md) mutation supports attaching reasons to returns and replacements. Reasons can be provided at two levels:

-   **Global** - a single `reason` (free text) and `reasonReference` (`Page` ID) that applies to the entire return request. These are stored on the resulting `Fulfillment` object.
-   **Per fulfillment line** - a `reason` and `reasonReference` on each `OrderReturnFulfillmentLineInput`. These are stored on the individual `FulfillmentLine` objects, allowing different reasons for different items in the same return.

<a id="input-schema"></a>

### Input schema

```graphql
input OrderReturnProductsInput {
  orderLines: [OrderReturnLineInput!]
  fulfillmentLines: [OrderReturnFulfillmentLineInput!]
  amountToRefund: PositiveDecimal
  includeShippingCosts: Boolean
  refund: Boolean
  reason: String
  reasonReference: ID
}

input OrderReturnFulfillmentLineInput {
  fulfillmentLineId: ID!
  quantity: Int!
  replace: Boolean
  reason: String
  reasonReference: ID
}
```

<a id="output-types"></a>

### Output types

The reason data is persisted and available on the resulting fulfillment objects:

```graphql
type Fulfillment {
  # ... existing fields ...
  reason: String
  reasonReference: Page
}

type FulfillmentLine {
  # ... existing fields ...
  reason: String
  reasonReference: Page
}
```

<a id="example-return-with-reasons"></a>

### Example: Return with reasons

**Mutation**

```graphql
mutation OrderFulfillmentReturnProducts($orderId: ID!, $input: OrderReturnProductsInput!) {
  orderFulfillmentReturnProducts(order: $orderId, input: $input) {
    returnFulfillment {
      id
      status
      reason
      reasonReference {
        id
        title
      }
      lines {
        id
        quantity
        reason
        reasonReference {
          id
          title
        }
      }
    }
    replaceFulfillment {
      id
      status
      reason
      reasonReference {
        id
        title
      }
      lines {
        id
        quantity
        reason
        reasonReference {
          id
          title
        }
      }
    }
    replaceOrder {
      id
    }
    order {
      status
    }
    errors {
      field
      code
      message
    }
  }
}
```

**Variables**

```json
{
  "orderId": "T3JkZXI6NjAzYWI5OWMtY2EzZC00NzQwLWIxMDMtMjRhYzMyYmM0MjE2",
  "input": {
    "fulfillmentLines": [
      {
        "fulfillmentLineId": "RnVsZmlsbG1lbnRMaW5lOjI1MQ==",
        "quantity": 1,
        "replace": true,
        "reason": "Item arrived damaged",
        "reasonReference": "UGFnZToz"
      },
      {
        "fulfillmentLineId": "RnVsZmlsbG1lbnRMaW5lOjI1Mg==",
        "quantity": 1,
        "replace": false,
        "reason": "Wrong size",
        "reasonReference": "UGFnZTo0"
      }
    ],
    "orderLines": [],
    "includeShippingCosts": false,
    "refund": false,
    "reason": "Customer return request #1234",
    "reasonReference": "UGFnZTox"
  }
}
```

The mutation accepts the following reason-related arguments:

-   `input`:
    -   `reason` - Root free-text reason for the return. Stored on the resulting `Fulfillment`.
    -   `reasonReference` - Global `Page` ID representing the reason for the return. Must match the `PageType` set in the `returnReasonReferenceType` return setting. Stored on the resulting `Fulfillment`. See [return reasons configuration](#return-reasons-configuration) to learn more.
    -   `fulfillmentLines[].reason` - Per-line free-text reason for the return.
    -   `fulfillmentLines[].reasonReference` - Per-line `Page` ID. Overrides the global `reasonReference` for this specific line. Stored on the resulting `FulfillmentLine`.

<a id="validation-rules"></a>

### Validation rules

-   When `returnReasonReferenceType` is configured and the caller is a **staff user**, the **global** `reasonReference` is required.
-   **Per-line `reasonReference` is always optional** — for both staff and apps. Lines without it are stored with `null`. There is no inheritance from the root level.
-   **Apps** can omit `reasonReference` at any level, even when configuration requires it.
-   When `reasonReference` is provided (root or per-line), the referenced `Page` must belong to the `PageType` set in the `returnReasonReferenceType` setting. Otherwise, a `ValidationError` is raised.
-   The `refund` flag does **not** affect reason validation. Whether `refund` is `true` or `false`, the same validation rules apply. The return reason (why items are being sent back) is independent of the refund reason (tracked on `OrderGrantedRefund`).

note

Returns are gated by a **dedicated** setting, `returnReasonReferenceType`, which is independent of the `refundReasonReferenceType` used by the refund mutations. Configuring one does not configure the other. See [return reasons configuration](#return-reasons-configuration).

<a id="refund-reasons-configuration"></a>

## Refund reasons configuration

note

Available from 3.22. Per-line reason references for grant refund mutations added in 3.23. Return/replace reasons use a separate setting — see [return reasons configuration](#return-reasons-configuration).

Staff users with `MANAGE_SETTINGS` permission can access additional refunds configuration available in Dashboard and via API.

Configuration is global and reflected on every channel.

Currently, `RefundSettings` type controls a single behavior: whether the `reasonReference` is required when the refund is created or updated.

<a id="setting-refund-reason-type-requirement"></a>

### Setting Refund Reason Type Requirement

You can decide you want a strict policy to require a reason reference for every refund. Such reference will be a relation to a `Model` type. To enable that, you need first to create a Model Type dedicated for such refunds. You can do that in the Dashboard or by `pageTypeCreate` mutation.

Once you have a `PageType` ID, you can set it in the `refundSettingsUpdate` mutation.

**Mutation**

```graphql
mutation SetRefundTypeRequired($pageTypeId:ID!){
  refundSettingsUpdate(input:{
    refundReasonReferenceType:$pageTypeId
  }){
    errors{
      message
    }
  }
}
```

**Variables**

```json
{
  "pageTypeId": "your-id"
}

```

Now, every time a _staff user_ tries to create or update a refund (`transactionRequestAction`, `orderGrantRefundCreate`, `orderGrantRefundUpdate`), they will be required to provide a reason reference. The existing free-text "reason" field will still be available, but it will be optional.

Returns (`orderFulfillmentReturnProducts`) are governed by a separate `returnReasonReferenceType` setting, documented in [return reasons configuration](#return-reasons-configuration).

Note that permitted `App` will be able to omit this setting. For the _staff user_, the `ValidationError` will be raised if the reason reference is not provided. Apps can omit this field for more powerful workflows.

Apart from that, the `reasonReference` provided in mutations must be a valid `Page` ID of the type set in the `refundReasonReferenceType` setting. If not, a `ValidationError` will be raised as well.

<a id="supported-mutations"></a>

### Supported mutations

These mutations require `reasonReference` (for staff) when the **refund** reason type (`refundReasonReferenceType`) is configured:

| Mutation | Order-level | Per-line | Since |
| --- | --- | --- | --- |
| `transactionRequestAction` | `refundReasonReference` | \- | 3.22 |
| `orderGrantRefundCreate` | `reasonReference` | `lines[].reasonReference` | 3.22 (order-level), 3.23 (per-line) |
| `orderGrantRefundUpdate` | `reasonReference` | `addLines[].reasonReference` | 3.22 (order-level), 3.23 (per-line) |

This mutation requires `reasonReference` (for staff) when the **return** reason type (`returnReasonReferenceType`) is configured:

| Mutation | Order-level | Per-line | Since |
| --- | --- | --- | --- |
| `orderFulfillmentReturnProducts` | `reasonReference` | `fulfillmentLines[].reasonReference` | 3.23 |

<a id="disabling-refund-reason-type-requirement"></a>

### Disabling refund reason type requirement

To opt-out from this strict policy, staff user can disable it in the Dashboard or by running the `refundReasonReferenceClear` mutation:

```graphql
mutation DisableRefundTypeRequired {
  refundReasonReferenceClear {
    errors {
      message
    }
  }
}
```

<a id="return-reasons-configuration"></a>

## Return reasons configuration

note

Available from 3.23

Returns use a configuration that is **separate** from refunds. The `ReturnSettings` type controls whether a `reasonReference` is required when products are returned via [`orderFulfillmentReturnProducts`](/api-reference/orders/mutations/order-fulfillment-return-products.md). Like refund settings, it is global and reflected on every channel, and requires the `MANAGE_SETTINGS` permission.

You can read the current setting with the `returnSettings` query:

```graphql
query {
  returnSettings {
    reasonReferenceType {
      id
      name
    }
  }
}
```

<a id="setting-return-reason-type-requirement"></a>

### Setting Return Reason Type Requirement

As with refunds, you first create a dedicated `Model` (Page) type (in the Dashboard or via `pageTypeCreate`), then set its ID with the `returnSettingsUpdate` mutation:

**Mutation**

```graphql
mutation SetReturnTypeRequired($pageTypeId: ID!) {
  returnSettingsUpdate(input: {
    returnReasonReferenceType: $pageTypeId
  }) {
    returnSettings {
      reasonReferenceType { id }
    }
    errors {
      field
      message
      code
    }
  }
}
```

**Variables**

```json
{
  "pageTypeId": "your-id"
}
```

Once configured, a _staff user_ calling `orderFulfillmentReturnProducts` must provide a global `reasonReference` whose `Page` belongs to the configured type; otherwise a `ValidationError` is raised. Per-line `reasonReference` remains optional. Apps may always omit it. This is independent of the `refund` flag.

<a id="disabling-return-reason-type-requirement"></a>

### Disabling return reason type requirement

To opt out, run the `returnReasonReferenceClear` mutation (or use the Dashboard):

```graphql
mutation DisableReturnTypeRequired {
  returnReasonReferenceClear {
    errors {
      message
    }
  }
}
```
