# Rewards API

{% hint style="warning" %}
**This is a DRAFT version and is subject to changes.**

This document has been created as a preliminary version to gather feedback, suggestions, and input from all parties involved.
{% endhint %}

## Glossary

| Term            | Definition                                                                                                |
| --------------- | --------------------------------------------------------------------------------------------------------- |
| PORTOS          | The cash register solution developed by Nine Digit, s.r.o.                                                |
| External system | The 3rd party system (such as e-shop or food delivery platform) from which rewards are fetched to PORTOS. |
| Venue           | E.g. store or restaurant.                                                                                 |
| Cashier         | The Venue personnel.                                                                                      |
| Customer        | Person who purchases the goods.                                                                           |
| Reward          | The coupon or discount that customer can be rewarded with.                                                |
| Product         | The product that is being purchased by customer.                                                          |

## How it works

The *Rewards API* is a mechanism that allows importing rewards (such as coupons, deals or discounts) to the PORTOS system.

The PORTOS system acts as an "client" initiating HTTP request targeting the *external system*, that acts as an "server" and handles the following requests:

1. Fetch available rewards
2. Claim reward(s)

The PORTOS system expects that responses from *external system* follows data format described below.&#x20;

## Fetch available rewards

<mark style="color:blue;">`GET`</mark> `{external-system}{get-rewards-url}`

The PORTOS system will fetch available rewards from the external system, by initiating GET HTTP request to the external system address. External system returns response of type [`CustomerRewards`](#customerrewards), that contains the collection of rewards, that customer can be rewarded with.

#### Query Parameters

| Name                                      | Type   | Description                                                                                   |
| ----------------------------------------- | ------ | --------------------------------------------------------------------------------------------- |
| version<mark style="color:red;">\*</mark> | string | The version of the exchange protocol. For versions 1.X, value is set to `1`.                  |
| key<mark style="color:red;">\*</mark>     | string | The API key.                                                                                  |
| customerId                                | string | The customer identifier. E.g. the serial number of the loyalty card, if provided by customer. |

#### Headers

| Name            | Type   | Description                                                                           |
| --------------- | ------ | ------------------------------------------------------------------------------------- |
| Accept-Language | string | Specifies the desired localization for receiving messages from the API. E.g. `sk-SK`. |

{% tabs %}
{% tab title="200 " %}
{% code overflow="wrap" %}

```json
{
    "customer":
    {
        "displayName": "John Doe",
        "email": "example@portos.sk",
        "points": 1281
    },
    "maxApplicableRewards": null,
    "rewards":
    [
        {
            "id": "dda658c8-7ca1-4993-9212-f1bcd992e638",
            "title": "Najlacnejšia pizza za jeden cent!",
            "description": "Získajte najlacnejšiu pizzu za jeden cent. Zľava platí pri nákupe nad 20 eur.",
            "conditions":
            [
                {
                    "purchase":
                    {
                        "minAmountIncludingVat": 20
                    }
                }
            ],
            "items":
            [
                {
                    "target": "purchaseItem",
                    "discountType": "absolute",
                    "discountAmount": 0.01,
                    "purchaseItemLookupMode": "cheapest",
                    "purchaseItemFilter":
                    {
                        "articleCategoryLabels":
                        [
                            "PIZ"
                        ],
                        "maxQuantity": 1
                    }
                }
            ]
        },
        {
            "id": "0214d09d-844f-4677-a449-829ddbf5d1cc",
            "title": "Vymeňte 1000 bodov za 5 eurovú zľavu",
            "items":
            [
                {
                    "target": "purchase",
                    "discountType": "absolute",
                    "discountAmount": 5
                }
            ],
            "priceInPoints": 1000
        }
    ]
}
```

{% endcode %}
{% endtab %}

{% tab title="401 The bearer authorization token is invalid." %}
{% code overflow="wrap" %}

```json
{
  "message": "This is the optional message describing the error."
}
```

{% endcode %}
{% endtab %}

{% tab title="403 The orders cannot be fetched. The reason may be provided in body." %}
{% code overflow="wrap" %}

```json
{
  "message": "A loyalty card is required for receiving rewards.",
  "code": "CUSTOMER_ID_REQUIRED"
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Claim reward(s)

<mark style="color:green;">`POST`</mark> `{external-system}{claim-rewards-url}`

After the reward is confirmed by Cashier trough the PORTOS cash register system user interface, the PORTOS system will initiate the POST HTTP request to the external system, to mark reward(s) as claimed.

#### Query Parameters

| Name                                      | Type   | Description                                                                  |
| ----------------------------------------- | ------ | ---------------------------------------------------------------------------- |
| version<mark style="color:red;">\*</mark> | string | The version of the exchange protocol. For versions 1.X, value is set to `1`. |
| key<mark style="color:red;">\*</mark>     | string | The API key.                                                                 |

#### Headers

| Name            | Type   | Description                                                                           |
| --------------- | ------ | ------------------------------------------------------------------------------------- |
| Accept-Language | string | Specifies the desired localization for receiving messages from the API. E.g. `sk-SK`. |

#### Request Body

| Name                                        | Type      | Description                                         |
| ------------------------------------------- | --------- | --------------------------------------------------- |
| rewardIds<mark style="color:red;">\*</mark> | string\[] | Non-empty collection of claimed reward identifiers. |

{% tabs %}
{% tab title="200 The reward usage has been acknowledged by the external system." %}
{% code overflow="wrap" %}

```json
/* No response body is required by PORTOS. */
```

{% endcode %}
{% endtab %}

{% tab title="401 The bearer authorization token is invalid." %}

```json
{
  "message": "This is the optional message describing the error."
}
```

{% endtab %}

{% tab title="403 Error when marking reward as used. E.g. reward has already been used." %}

```json
{
  "message": "This reward is currently not available.",
  "code": "REWARD_USAGE_LIMIT_EXCEEDED",
  "rewardId": "29e61e3b-34ae-4c1f-9bd8-f4ef9925a1f8"
}
```

{% endtab %}
{% endtabs %}

## Configuration

To communicate with external system properly, the external system administrators must provide values stated below to the venue owners or PORTOS support.

| Value                               | Type     | Description                                                                                                             |
| ----------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------- |
| External system "get rewards" URL   | `string` | The URL hosted by external system, that will be requested by PORTOS to fetch rewards.                                   |
| External system "claim rewards" URL | `string` | The URL hosted by external system, that will be requested by PORTOS to claim rewards.                                   |
| API key                             | `string` | The venue-specific API key (authorization token) that will be placed in the `key` query parameter of all HTTP requests. |

## Data model

The rewards API data model is described below.

{% hint style="info" %}
All prices include taxes.
{% endhint %}

### CustomerRewards

The collection of rewards available for given customer.

<table><thead><tr><th>Name</th><th width="171">Type</th><th width="102" data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><code>rewards</code></strong></td><td><a href="#reward"><code>Reward</code></a><code>[]</code></td><td>true</td><td>The collection of available rewards.</td></tr><tr><td>customer</td><td><a href="#customer"><code>Customer</code></a></td><td>false</td><td>The information about customer, if available.</td></tr><tr><td><code>maxApplicableRewards</code></td><td><code>int</code></td><td>false</td><td>The maximum number of applicable rewards per given purchase.</td></tr></tbody></table>

### Customer

Information about customer.

<table><thead><tr><th>Name</th><th width="171">Type</th><th width="102" data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><code>displayName</code></strong></td><td><code>string</code></td><td>true</td><td>The display name of customer or customer loyalty account.</td></tr><tr><td><strong><code>points</code></strong></td><td><code>number</code></td><td>true</td><td>Represents number of loyalty points associated with customer account.</td></tr><tr><td><code>firstName</code></td><td><code>string</code></td><td>false</td><td>First name.</td></tr><tr><td><code>lastName</code></td><td><code>string</code></td><td>false</td><td>Last name.</td></tr><tr><td><code>email</code></td><td><code>string</code></td><td>false</td><td>E-mail address that can be used when sending receipt in e-mail form.</td></tr><tr><td><code>maxApplicableRewards</code></td><td><code>int</code></td><td>false</td><td>The maximum number of rewards that can be applied per purchase. The rewards can also be  limited by specifying available customers loyalty points and price in points of reward.</td></tr></tbody></table>

### Reward

Represents the reward.

<table><thead><tr><th>Name</th><th width="176">Type</th><th width="102" data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><code>id</code></strong></td><td><code>string</code></td><td>true</td><td>Unique reward identifier.</td></tr><tr><td><strong><code>title</code></strong></td><td><code>string</code></td><td>true</td><td>Reward title.</td></tr><tr><td><code>description</code></td><td><code>string</code></td><td>false</td><td>Short reward description.</td></tr><tr><td><code>conditions</code></td><td><a href="#rewardcondition"><code>RewardCondition</code></a><code>[]</code></td><td>false</td><td>Conditions that need to be fulfilled in order for the customer to claim the reward.</td></tr><tr><td><strong><code>items</code></strong></td><td><a href="#rewarditem"><code>RewardItem</code></a><code>[]</code></td><td>true</td><td>Collection of one or more items associated with given reward.</td></tr><tr><td><code>activationDate</code></td><td><code>string</code></td><td>false</td><td>If specified, the reward becomes applicable only after the specified date and time in ISO_8601 format, expressed in UTC.</td></tr><tr><td><code>expirationDate</code></td><td><code>string</code></td><td>false</td><td>If specified, the reward is applicable only before the specified date and time in ISO_8601 format, expressed in UTC.</td></tr><tr><td><code>minPurchaseAmountIncludingVat</code></td><td><code>number</code></td><td>false</td><td>If specified, represents the minimum purchase amount required to claim the reward.</td></tr><tr><td><code>priceInPoints</code></td><td><code>number</code></td><td>false</td><td>Specifies number of loyalty points subtracted from customer's loyalty account once the reward is claimed.</td></tr><tr><td><code>remainingUsage</code></td><td><code>number</code></td><td>false</td><td>Specifies maximum number of times can this reward be claimed by any customer.</td></tr><tr><td><code>remainingCustomerUsage</code></td><td><code>number</code></td><td>false</td><td>Specifies maximum number of times can this reward be claimed by given customer.</td></tr></tbody></table>

### RewardCondition

<table><thead><tr><th width="237">Name</th><th width="197">Type</th><th width="102" data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>purchase</code></td><td><a href="#rewardpurchasecondition"><code>RewardPurchaseCondition</code></a></td><td>false</td><td>Optional conditions related to purchase.</td></tr></tbody></table>

### RewardPurchaseCondition

<table><thead><tr><th>Name</th><th width="176">Type</th><th width="102" data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>minAmountIncludingVat</code></td><td><code>number</code></td><td>false</td><td>If specified, represents the minimum purchase amount required to claim the reward.</td></tr></tbody></table>

### RewardItem

Single reward item.

<table><thead><tr><th>Name</th><th width="171">Type</th><th width="102" data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><code>target</code></strong></td><td><code>string</code></td><td>true</td><td>One of <a href="#rewarditemtarget"><code>RewardItemTarget</code></a>.</td></tr><tr><td><code>productFilter</code></td><td><a href="#rewarditemproductfilter"><code>RewardItemProductFilter</code></a></td><td>false</td><td>Required, when <code>target</code> is <code>product</code>. Must be null otherwise.</td></tr><tr><td><code>purchaseItemFilter</code></td><td><a href="#rewarditempurchaseitemfilter"><code>RewardItemPurchaseItemFilter</code></a></td><td>false</td><td>Required, when <code>target</code> is <code>purchaseItem</code>. Must be null otherwise.</td></tr><tr><td><code>purchaseItemLookupMode</code></td><td><code>string</code></td><td>false</td><td>One of <code>PurchaseItemLookupMode.</code> when <code>target</code> is <code>purchaseItem</code>. Must be null otherwise.</td></tr><tr><td><strong><code>discountType</code></strong></td><td><code>string</code></td><td>true</td><td>One of <a href="#rewarditemdiscounttype"><code>RewardItemDiscountType</code></a>. If <code>target</code> is equal to <code>purchase</code>, value must be <code>prcentage</code> or <code>absolute</code>.</td></tr><tr><td><code>discountAmount</code></td><td><code>number</code></td><td>false</td><td>Positive, non-zero number. Minimal value is <code>0.01</code>. Required, when <code>discountType</code> is <code>absolute</code> or <code>relative</code>. Must be null otherwise. </td></tr><tr><td><code>discountRate</code></td><td><code>number</code></td><td>false</td><td>Positive, non-zero number, with value between <code>0.01</code> and <code>100</code>. Required, when <code>discountType</code> is <code>percentage</code>.  Must be null otherwise.</td></tr></tbody></table>

### RewardItemDiscountType

List of known discount types:

<table><thead><tr><th width="205">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>percentage</code></td><td>Reward provides a percentage discount. </td></tr><tr><td><code>absolute</code></td><td>Reward provides a discount that is specified by a fixed or absolute value rather than a percentage.</td></tr><tr><td><code>relative</code></td><td>Reward provides a discount by subtracting a specific value from the original retail price of a product or service.</td></tr></tbody></table>

### RewardItemTarget

The target that reward item applies to.

<table><thead><tr><th width="210">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>purchaseItem</code></td><td>Reward provides a discount to single purchase item. Purchase item can be specified by <a href="#rewarditempurchaseitemfilter"><code>RewardItemPurchaseItemFilter</code></a>. If no suitable product is found in the purchase, reward is not applied.</td></tr><tr><td><code>product</code></td><td>Reward provides a discount for a certain product, specified by <a href="#rewarditemproductfilter"><code>RewardItemProductFilter</code></a>. If given product is not initially included in the purchase, it will be added to the purchase automatically, so that the discount can be applied.</td></tr><tr><td><code>purchase</code></td><td>Reward applies a discount to entire purchase</td></tr></tbody></table>

### RewardItemProductFilter

Model that uniquely identifies product.

<table><thead><tr><th width="199">Name</th><th width="171">Type</th><th width="102" data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>pluId</code></td><td><a href="/pages/uaghKxVt9eOU4jW7e9Ht#pluid"><code>PluId</code></a></td><td>false</td><td>At least one of identifier fields (<code>pluId</code> or <code>id</code>) must be specified.</td></tr><tr><td><code>id</code></td><td><code>string</code></td><td>false</td><td>At least one of identifier fields (<code>pluId</code> or <code>id</code>) must be specified.</td></tr></tbody></table>

### RewardItemPurchaseItemFilter

Model that selects purchase item.

<table><thead><tr><th width="199">Name</th><th width="171">Type</th><th width="102" data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>pluIds</code></td><td><a href="/pages/uaghKxVt9eOU4jW7e9Ht#pluid"><code>PluId</code></a><code>[]</code></td><td>false</td><td><code>TicketItem.Plu.Code</code> and <code>TicketItem.Plu.StockName</code>must be equal to one of provided values, if value is not null or empty.</td></tr><tr><td><code>articleCategoryLabels</code></td><td><code>string[]</code></td><td>false</td><td><code>TicketItem.Plu.ArticleCategoryLabel</code> must be equal to one of provided values, if value is not null or empty.</td></tr><tr><td><code>minUnitPriceIncludingVat</code></td><td><code>number</code></td><td>false</td><td>If specified, <code>TicketItem.UnitPriceIncludingVat</code> must be greater than or equal to provided value.</td></tr><tr><td><code>maxUnitPriceIncludingVat</code></td><td><code>number</code></td><td>false</td><td>If specified, <code>TicketItem.UnitPriceIncludingVat</code> must be less than or equal to provided value.</td></tr><tr><td><code>minQuantity</code></td><td><code>number</code></td><td>false</td><td>If specified, <code>TicketItem.Quantity</code> must be greater than or equal to provided value.</td></tr><tr><td><code>maxQuantity</code></td><td><code>number</code></td><td>false</td><td>If specified, <code>TicketItem.Quantity</code> must be less than or equal to provided value.</td></tr></tbody></table>

### PurchaseItemLookupMode

Specifies, what strategy is used to look up single purchase item among all items in purchase, that reward will be applied to.&#x20;

<table><thead><tr><th width="205">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>cheapest</code></td><td>Prefers least expensive product, based on it's unit price.</td></tr><tr><td><code>mostExpensive</code></td><td>Prefers most expensive product, based on it's unit price.</td></tr></tbody></table>

## Error models

### Error

Error model received for non-2XX HTTP statuses.

<table><thead><tr><th>Name</th><th width="171">Type</th><th width="102" data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><code>message</code></strong></td><td><code>string</code></td><td>true</td><td>Human readable message describing the error.</td></tr><tr><td><code>rewardId</code></td><td><code>string</code></td><td>false</td><td>Id of <a href="#reward"><code>Reward</code></a>, if error is reward-specific.</td></tr><tr><td><code>code</code></td><td><code>string</code></td><td>false</td><td>Available, if error matches one of <a href="#errorcode">known error codes</a>. </td></tr></tbody></table>

### ErrorCode

The third-party server can return an [`Error`](#error) with one of the following codes:

<table><thead><tr><th width="258.3333333333333">Error code</th><th>Description</th></tr></thead><tbody><tr><td><code>CUSTOMER_ID_REQUIRED</code></td><td>Should be used when PORTOS attempts to fetch rewards without providing loyalty card id, but loyalty card is required by the third-party server.</td></tr><tr><td><code>UNKNOWN_CUSTOMER_ID</code></td><td>Customer loyalty card is not recognized.</td></tr><tr><td><code>REWARD_NOT_FOUND</code></td><td>Reward is not found.</td></tr><tr><td><code>REWARD_NOT_AVAILABLE</code></td><td>Reward is currently not available. E.g. current date and time does not match with <a href="#reward"><code>Rewards</code></a> <code>ActivationDate</code> and <code>ExpirationDate</code>.</td></tr><tr><td><code>REWARD_USAGE_LIMIT_EXCEEDED</code></td><td>The reward usage limit has been exceeded.</td></tr><tr><td><code>REWARD_CUSTOMER_USAGE_LIMIT_EXCEEDED</code></td><td>The reward usage limit has been exceeded for the specified customer.</td></tr><tr><td><code>INSSUFICIENT_LOYALTY_POINTS</code></td><td>The customer lacks sufficient loyalty points to claim the reward.</td></tr></tbody></table>

## Versioning

This API uses [semantic versioning](https://semver.org/). Given a version number `MAJOR.MINOR`, increment the:

* MAJOR version: incompatible API changes are introduced.
* MINOR version: functionality is added in a backward compatible manner.

## Changelog

### Version 1.0-beta.2 (2023-05-29)

* Added:
  * Accept-Language header added to HTTP requests.
* Changed:
  * `RewardRule` renamed to `RewardCondition`.

### Version 1.0-beta.1 (2023-05-26)

* Initial version.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developers.portos.sk/integrations/rewards-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
