# Building your transaction fee functionality

## Prerequisites

Before creating a transaction fee model, ensure the following:

* To create fee models, you must have active Upvest API client credentials with the required `transaction_fees:admin` scopes enabled.
* You have determined the exact structure of the fees, your desired tier thresholds and whether the fees will be absolute (fixed cash amounts) or relative (basis points of order value).


### Step 1: Create your transaction fee model

Use `POST /transaction_fees/models` to define your fee model. Fee models are **immutable** once created. You can create multiple fee models.

#### Flat absolute fee

Applies a simple flat fee per transaction regardless of the order value.

Example: 1 EUR fee per order

**`POST /transaction_fees/models`**

Request
```json
{
    "label": "absolute fee amount - 1 EUR per any order",
    "currency": "EUR",
    "charge_method": "COLLECTED_BY_UPVEST",
    "value_type": "ABSOLUTE",
    "application_type": "VOLUME",
    "base_amount_scope": "ORDER",
    "tiers": [
      {
  "tier_id": "0",
  "base_amount_from": "0.00",
  "fee_amount": "1.00"
      }
    ]
  }
```

Response
```json
{
  "id": "eb5ba93f-5dfe-4bf1-8571-4da0caacc80c",
  "created_at": "2021-07-21T14:10:00.00Z",
  "updated_at": "2021-07-21T14:10:00.00Z",
  "label": "absolute fee amount - 1 EUR per any order",
    "currency": "EUR",
    "charge_method": "COLLECTED_BY_UPVEST",
    "value_type": "ABSOLUTE",
    "application_type": "VOLUME",
    "base_amount_scope": "ORDER",
    "tiers": [
      {
  "tier_id": "0",
  "base_amount_from": "0.00",
  "fee_amount": "1.00"
      }
    ]
}
```

### Tiered absolute fee

The example below uses a tiered absolute fee to reward high-volume users with lower effective rates.

**Example tier structure for absolute fees**:

| **Tier** | **Thresholds (e.g., the order’s cash amount)** | **Transaction fee** |
|  --- | --- | --- |
| **0** | Up to €499.99 | €1.00 |
| **1** | €500.00 – €1,999.99 | €2.00 |
| **2** | €2,000.00 – €9,999.99 | €5.00 |
| **3** | €10,000 and above | €10.00 |


**`POST /transaction_fees/models`**

Request
```json
{
    "label": "absolute fee amount - tiered fee model",
    "currency": "EUR",
    "charge_method": "COLLECTED_BY_UPVEST",
    "value_type": "ABSOLUTE",
    "application_type": "VOLUME",
    "base_amount_scope": "ORDER",
    "tiers": [
      {
  "tier_id": "0",
  "base_amount_from": "0",
  "fee_amount": "1.00"
      },
      {
  "tier_id": "1",
  "base_amount_from": "500.00",
  "fee_amount": "2.00"
      },
      {
  "tier_id": "2",
  "base_amount_from": "2000.00",
  "fee_amount": "5.00"
      },
      {
  "tier_id": "3",
  "base_amount_from": "10000.00",
  "fee_amount": "10.00"
      }
    ]
}
```

Response
```json
{
  "id": "eb5ba93f-5dfe-4bf1-8571-4da0caacc80c",
  "created_at": "2021-07-21T14:10:00.00Z",
  "updated_at": "2021-07-21T14:10:00.00Z",
      "label": "absolute fee amount - tiered fee model",
    "currency": "EUR",
    "charge_method": "COLLECTED_BY_UPVEST",
    "value_type": "ABSOLUTE",
    "application_type": "VOLUME",
    "base_amount_scope": "ORDER",
    "tiers": [
      {
  "tier_id": "0",
  "base_amount_from": "0",
  "fee_amount": "1.00"
      },
      {
  "tier_id": "1",
  "base_amount_from": "500.00",
  "fee_amount": "2.00"
      },
      {
  "tier_id": "2",
  "base_amount_from": "2000.00",
  "fee_amount": "5.00"
      },
      {
  "tier_id": "3",
  "base_amount_from": "10000.00",
  "fee_amount": "10.00"
      }
    ]
}
```

### Flat relative fee without minimum or maximum specified

Applies a simple flat fee percentage regardless of the order value.

Example: 1 % fee per order

**`POST /transaction_fees/models`**

Request
```json

{
    "label": "1% transaction fee",
    "currency": "EUR",
    "charge_method": "COLLECTED_BY_UPVEST",
    "value_type": "RELATIVE",
    "application_type": "VOLUME",
    "base_amount_scope": "ORDER",
    "tiers": [
      {
  "tier_id": "0",
  "base_amount_from": "0.00",
  "fee_bps": "100"
      }
    ]
  }
```

Response
```json
{
  "id": "eb5ba93f-5dfe-4bf1-8571-4da0caacc80c",
  "created_at": "2021-07-21T14:10:00.00Z",
  "updated_at": "2021-07-21T14:10:00.00Z",
    "label": "1% transaction fee",
    "currency": "EUR",
    "charge_method": "COLLECTED_BY_UPVEST",
    "value_type": "RELATIVE",
    "application_type": "VOLUME",
    "base_amount_scope": "ORDER",
    "tiers": [
      {
  "tier_id": "0",
  "base_amount_from": "0.00",
  "fee_bps": "100"
      }
    ]
}
```

### Flat relative fee with minimum and maximum specified

Applies a flat fee percentage with minimum and maximum fee amount.

Example: 1 % fee per order, not less than 1 EUR, not more than 100 EUR.

**`POST /transaction_fees/models`**

Request
```json
{
    "label": "1% transaction fee, min 1 EUR, max 100 EUR",
    "currency": "EUR",
    "charge_method": "COLLECTED_BY_UPVEST",
    "value_type": "RELATIVE",
    "application_type": "VOLUME",
    "base_amount_scope": "ORDER",
    "tiers": [
      {
  "tier_id": "0",
  "base_amount_from": "0.00",
  "fee_bps": "100",
  "min_fee_amount":"1.00",
  "max_fee_amount":"100.00"
      }
     ]
  }
```

Response
```json
{
  "id": "eb5ba93f-5dfe-4bf1-8571-4da0caacc80c",
  "created_at": "2021-07-21T14:10:00.00Z",
  "updated_at": "2021-07-21T14:10:00.00Z",
  "label": "1% transaction fee, min 1 EUR, max 100 EUR",
  "currency": "EUR",
  "charge_method": "COLLECTED_BY_UPVEST",
  "value_type": "RELATIVE",
  "application_type": "VOLUME",
  "base_amount_scope": "ORDER",
  "tiers": [
    {  "tier_id": "0",
       "base_amount_from": "0.00",
       "fee_bps": "100",
       "min_fee_amount":"1.00",
       "max_fee_amount":"100.00"
      }
     ]
}
```

### Tiered relative fee models

#### **Application to the marginal increase on top of the tier threshold**

Depending on the order volume, each next EUR is charged less fee than the previous.

Example:

| **Tier** | **Thresholds (e.g., the order’s cash amount)** | **Fee basis point (bps)** |
|  --- | --- | --- |
| **0** | First €5000.00 of the order volume | 300 |
| **1** | Amount exceeding €5000.00, but less than €10000.00 | 250 |
| **2** | Amount exceeding € 10.000.00 | 200 |


**`POST /transaction_fees/models`**

Request
```json
{
    "label": "Marginal relative tiered fee model",
    "currency": "EUR",
    "charge_method": "CHARGED_BY_CLIENT",
    "value_type": "RELATIVE",
    "application_type": "MARGINAL",
    "base_amount_scope": "ORDER",
    "tiers": [
      {
  "tier_id": "0",
  "base_amount_from": "0.00",
  "fee_bps": "300"
      },
      {
  "tier_id": "1",
  "base_amount_from": "5000.00",
  "fee_bps": "250"
      },
      {
   "tier_id": "2",
   "base_amount_from": "10000.00",
   "fee_bps": "200"
        }
      ]
    }
```

Response
```json
{
  "id": "eb5ba93f-5dfe-4bf1-8571-4da0caacc80c",
  "created_at": "2021-07-21T14:10:00.00Z",
  "updated_at": "2021-07-21T14:10:00.00Z",
  "label": "Tiered relative fee model with lower/upper bounds",
  "currency": "EUR",
  "charge_method": "CHARGED_BY_CLIENT",
  "value_type": "RELATIVE",
  "application_type": "MARGINAL",
  "base_amount_scope": "ORDER",
  "tiers": [
    {
"tier_id": "0",
"base_amount_from": "0.00",
"fee_bps": "300",
"min_fee_amount":"1.00"
    },
    {
"tier_id": "1",
"base_amount_from": "5000.00",
 	"fee_bps": "250",
"min_fee_amount": "150.00"
    },
    {
 "tier_id": "2",
 "base_amount_from": "10000.00",
 "fee_bps": "200",
 "min_fee_amount": "250.00",
 "max_fee_amount": "300.00"
      }
    ]
}
```

#### Application to the whole order amount

Example:

| **Tier** | **Thresholds (e.g., the total order’s cash amount)** | **Fee basis point (bps)** | **Fee limits** |
|  --- | --- | --- | --- |
| **0** | Up to €4999.99 | 300 | Min: 1 |
| **1** | €5000.00 – €9.999,99 | 250- | Min: 150 |
| **2** | 10.000.00 and above | 200 | Min: 250Max: 300 |


**`POST /transaction_fees/models`**

Request
```json
{
    "label": "Tiered relative fee model with lower/upper bounds",
    "currency": "EUR",
    "charge_method": "CHARGED_BY_CLIENT",
    "value_type": "RELATIVE",
    "application_type": "VOLUME",
    "base_amount_scope": "ORDER",
    "tiers": [
      {
  "tier_id": "0",
  "base_amount_from": "0.00",
  "fee_bps": "300",
  "min_fee_amount":"1.00"
      },
      {
  "tier_id": "1",
  "base_amount_from": "5000.00",
  "fee_bps": "250",
  "min_fee_amount": "150.00"
      },
      {
   "tier_id": "2",
   "base_amount_from": "10000.00",
   "fee_bps": "200",
   "min_fee_amount": "250.00",
   "max_fee_amount": "300.00"
        }
      ]
    }
```

Response
```json
{
  "id": "eb5ba93f-5dfe-4bf1-8571-4da0caacc80c",
  "created_at": "2021-07-21T14:10:00.00Z",
  "updated_at": "2021-07-21T14:10:00.00Z",
  "label": "Tiered relative fee model with lower/upper bounds",
  "currency": "EUR",
  "charge_method": "CHARGED_BY_CLIENT",
  "value_type": "RELATIVE",
  "application_type": "VOLUME",
  "base_amount_scope": "ORDER",
  "tiers": [
    {
"tier_id": "0",
"base_amount_from": "0.00",
"fee_bps": "300",
"min_fee_amount":"1.00"
    },
    {
"tier_id": "1",
"base_amount_from": "5000.00",
 	"fee_bps": "250",
"min_fee_amount": "150.00"
    },
    {
 "tier_id": "2",
 "base_amount_from": "10000.00",
 "fee_bps": "200",
 "min_fee_amount": "250.00",
 "max_fee_amount": "300.00"
      }
    ]
}
```

### Request parameters

| Field | Required | Description |
|  --- | --- | --- |
| `label` | **Required** | A human-readable name for the fee model.  The label should be unique, descriptive, and clear to aid in referencing the transaction fee model. |
| `currency` | **Required** | The currency of the fee. Alphabetic three-letter [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code. This field accepts either `EUR` or `GBP`. |
| `charge_method` | **Required** | This field determines which party collects the fee. The value of this field depends on the funding arrangement you have with Upvest.  For pre-funding clients, always set `charge_method` equal to `CHARGED_BY_UPVEST`. For post-trade settlement clients, always set `charge_method` equal to `CHARGED_BY_CLIENT`. |
| `value_type` | **Required** | The `value_type` field determines the type of fee to be collected. Set this equal to either:   - `ABSOLUTE` for fixed, flat cash fees.  - `RELATIVE` for basis-point-based fees. |
| `application_type` | **Required** | Determines how the fee will be applied to the transaction.  - `VOLUME`: the full order value is assessed against the tier thresholds. The fee in the tiers is applied to the total volume of the order.  - `MARGINAL`: each portion of the order value is charged at the rate of the tier it falls within. Use this to set up a marginal/progressive fee structure. Only applicable to tiered fee models. |
| `base_amount_scope` | **Required** | Set this field equal to `ORDER`. Specifies that fee tiers are evaluated against the total order value. |
| `tiers` | **Required** | Array of tier objects. At least one tier must be specified. A single-tier model functions as a simple flat fee. |
| `tiers[].tier_id` | **Required** | A string identifier for the tier (e.g. `"0"`, `"1"`, `"2"`). |
| `tiers[].base_amount_from` | **Required** | The lower boundary (inclusive) of the tier. The first tier must always start at `"0"`. |
| `tiers[].fee_amount` | **Conditional** | Required for `ABSOLUTE` models. The fixed, flat fee amount charged when the order falls within this tier. |
| `tiers[].fee_bps` | **Conditional** | Required for `RELATIVE` models. The fee rate in basis points applied to the order value for this tier. |
| `tiers[].min_fee_amount` | Optional | Minimum fee amount for a `RELATIVE` tier. The calculated fee will not fall below this value. |
| `tiers[].max_fee_amount` | Optional | Maximum fee amount for a `RELATIVE` tier. The calculated fee will not exceed this value. |


Once you have created a fee model, the response returns an `id`. This is the  `transaction_fee_model_id` for use in order requests and ex-ante cost reports.

### Step 2 (when applicable): Apply a fee model in the ex-ante cost report

For ex-ante reporting, you can use the POST /reports endpoint to include your transaction fee model in the reporting.

**`POST /reports`**

Request
```json
{
  "type": "ORDER_EX_ANTE_COST",
  "order": {
    "user_id": "d1a4be99-8bb6-4e78-b897-8168f6823ab5",
    "account_id": "c5034305-c441-4711-adbf-93cfbc13a695",
    "order_type": "MARKET",
    "side": "BUY",
    "instrument_id": "LU0274208692",
    "instrument_id_type": "ISIN",
    "currency": "EUR",
    "quantity": "15"
  },
  "fees": [
    {
      "type": "TRANSACTION_FEE_BUY",
      "transaction_fee_model_id": "eb5ba93f-5dfe-4bf1-84da-0caacc80c000"
    },
    {
      "type": "TRANSACTION_FEE_SELL",
      "value_type": "ABSOLUTE",
      "cash_amount": "0.95",
      "currency": "EUR"
    },
    {
      "type": "ANNUAL_AUM_BASED_FEE",
      "value_type": "RELATIVE",
      "bps": "37"
    }
  ],
  "inducements": [
    {
      "value_type": "ABSOLUTE",
      "cash_amount": "0.10",
      "currency": "EUR"
    }
  ]
}
```

### Step 3: Apply a fee model to an order

When placing an order, reference your `transaction_fee_model_id` in the `fee_configuration` array. Upvest will calculate and apply the appropriate fee based on the order value and the configured tiers.

If no fee model is assigned and an explicit fee is instead provided via `fee_configuration`, the explicit fee takes precedence.

**`POST /orders`**

Request
```json
{
  "user_id": "2dedfeb0-58cd-44f2-ae08-0e41fe0413d9",
  "account_id": "debf2026-f2da-4ff0-bb84-92e45babb1e3",
  "cash_amount": "1000",
  "currency": "EUR",
  "side": "BUY",
  "instrument_id": "US0378331005",
  "instrument_id_type": "ISIN",
  "order_type": "MARKET",
  "user_instrument_fit_acknowledgement": true,
  "limit_price": "",
  "stop_price": "",
  "fee_configuration": [
    {
      "type": "TRANSACTION_FEE_BUY",
      "transaction_fee_model_id": "eb5ba93f-5dfe-4bf1-84da-0caacc80c000"
    }
  ]
}
```

The `fee_configuration.type` must match the type of order:

* Set `TRANSACTION_FEE_BUY` fee type for BUY orders
* Set `TRANSACTION_FEE_SELL` fee type for SELL orders.


If there is a mismatch, the order will be rejected.

### Step 4: Monitor the Cash transactions event webhook for fee details

After an order with a transaction fee is executed, fees are reflected in the `CASH_TRANSACTION.EXECUTED` webhook events. The `fees` array is populated with the fee details.
The value reflected in the `delta` field is dependent on your funding setup with Upvest.

- **Pre-funding clients**: The `delta` reflects the net cash movement inclusive of both taxes and fees.
- **Post-trade settlement clients**: The `delta` reflects the net cash movement for the order inclusive of taxes but does not include the impact of the fee.


For more information, refer to the [Webhooks and transaction fee details](/products/tol/guides/fees/fees_transaction_fees_webhooks) section.

The order event webhook includes the `transaction_fee_model_id` but not a detailed breakdown of the transaction fee. Also, order execution events do not include details on transaction fees.