Integration Guide — Advanced Discount
Service: Core API (Soba back-end)
Authentication: Bearer token (JWT) and tenant context — see Combined Login
Introduction
This guide describes integration with the API to manage advanced discounts (rules with multiple models: “buy X get Y free” offers, discount on document total, discount on line, campaign).
The request body is polymorphic: the discriminator property is templateType. The API requires templateType in the JSON and interprets the rest of the body (especially object) based on this value (see Conditional Payload Dependencies).
After validation, the service normalizes targetObject and fills target with the already processed customer filter; generally send targetObject in the request and treat target as derived output.
Available Endpoints
| Method | URL | Brief Description |
|---|---|---|
| GET | /gateway/advanced-discount | Lists discounts with optional pagination (page, pageSize); see pagination rules. |
| GET | /gateway/advanced-discount/{id} | Retrieves a discount by unique identifier (UUID). |
| GET | /gateway/advanced-discount/next-order | Returns the next suggested order number. |
| POST | /gateway/advanced-discount | Creates a discount; 200 OK response with created UUID in the body. |
| PUT | /gateway/advanced-discount/{id} | Updates; id in route and body must match; 204 No Content response. |
| DELETE | /gateway/advanced-discount/{id} | Deletes by unique identifier (UUID); 204 No Content response. |
List
URL: /gateway/advanced-discount — Method: GET
Get by Identifier
URL: /gateway/advanced-discount/{id} — Method: GET
Next Order
URL: /gateway/advanced-discount/next-order — Method: GET
Create
URL: /gateway/advanced-discount — Method: POST
Update
URL: /gateway/advanced-discount/{id} — Method: PUT
Delete
URL: /gateway/advanced-discount/{id} — Method: DELETE
Request Body Structure
Property names follow camelCase in JSON. The discriminator templateType is mandatory; without it, the request is rejected during deserialization.
| Field | Type | Description |
|---|---|---|
id | string (UUID) | On update, existing identifier; on create, can be empty — the server generates a new identifier. |
description | string | Required. |
active | bool | Active flag. |
includedTerminals | int[] | Terminals where the rule applies; see validation Terminals. |
discountOrder | int? | Application order. |
cumulative | bool | Allow stacking with other discounts. |
startDate | date-time? | Start of validity (date; see “past” validation). |
endDate | date-time? | End of validity. |
appliedTo | int | Where the rule applies: line (0) or document (1); see table in Enums. |
weekDaysAvailability | int (bitmask) | Days of the week when the rule is active; see Conditional Dependencies and value table. |
targetObject | object? | Target customer filter criteria (nested structure with logical operators and criteria). |
target | string? | Filled by the server from targetObject; not the focus of create/edit requests. |
templateType | int | Model discriminator; see table below. |
object | object | Model-specific content; structure depends on templateType. |
templateType and object shape
templateType | Model |
|---|---|
0 | Offers (buy X get Y free) |
1 | Discount on document total |
2 | Discount on document line |
3 | Campaign (multiple promotion lines) |
object — Offers model (templateType === 0)
| Field | Type | Description |
|---|---|---|
eligibility | int | 0 = item, 1 = family — interprets the meaning of IDs in eligibleIdList. |
eligibleIdList | string[] | Eligible references (item codes or family identifiers, per eligibility). |
triggerAmount | decimal | “Per each” quantity (trigger). |
offerAmount | decimal | Offered quantity. |
selectedOfferItemKeyId | string? | Offer item (optional). |
offerItemMayNotBePresent | bool | Whether the offer item may not be present in the sale. |
object — document total (templateType === 1)
| Field | Type | Description |
|---|---|---|
selectedCalcType | int | Calculation mode on the product (e.g., total above vs per each); the server does not validate this integer against the set of offer types used on the line. |
amount | decimal | Threshold / amount associated with the calculation mode. |
offerType | int | Offer type on document total; must be a valid value according to the Offer / line calculation types table. |
discount | decimal | Discount value (amount or percentage depending on offerType). |
activeAfterNumberOfDays | int | Days until voucher/discount becomes active (product semantics). |
expiresAfterNumberOfDays | int | Validity in days (product semantics). |
object — document line (templateType === 2)
| Field | Type | Description |
|---|---|---|
eligibility | int | Item (0) or family (1) for eligibleIdList. |
eligibleIdList | string[] | List of references; the service does not require non-empty in this model, but the business engine may require it. |
selectedCalcType | int | Calculation type on the line; must be a valid value according to the Offer / line calculation types table. |
discount | decimal | Discount (effective value validated as > 0). |
activeAfterNumberOfDays | int | Same as the total model, at line scale. |
expiresAfterNumberOfDays | int | Same. |
object — campaign (templateType === 3)
| Field | Type | Description |
|---|---|---|
campaignItems | array | List of campaign lines; see properties in the following table. |
campaignItems elements
| Field | Type | Notes |
|---|---|---|
promoId | int? | Optional. |
id | string | Campaign line identifier; mandatory non-empty in server validation. |
type | int | 0 = item, 1 = family / group. |
percentage, retailPriceHolder, fixedPrice, newPriceHolder, quantity, priceLine, … | numeric / string | Fields supporting the campaign discount type. |
discountType | int | Campaign discount type; mandatory and must be a value from the Campaign discount types table. |
afterQuantity, afterQuantityHolder, descriptionHolder | various | According to campaign scenario. |
Enums (numeric values in JSON)
templateType
| Value | Meaning |
|---|---|
0 | Offers (buy X get Y free) |
1 | Discount on document total |
2 | Discount on document line |
3 | Campaign |
appliedTo
| Value | Meaning |
|---|---|
0 | Line |
1 | Document |
Consistency recommendation with the product: with templateType 0 or 1 use appliedTo = 1 (document); with templateType 2 or 3 use appliedTo = 0 (line). The server accepts any defined value in the table above; incoherent combinations may not make sense at runtime.
weekDaysAvailability (bitmask of days)
| Value | Day |
|---|---|
1 | Monday |
2 | Tuesday |
4 | Wednesday |
8 | Thursday |
16 | Friday |
32 | Saturday |
64 | Sunday |
Combine with bitwise OR sum (e.g., weekdays = 31; all days = 127).
Eligibility in offers and line (eligibility)
| Value | Meaning |
|---|---|
0 | Item |
1 | Family |
Offer and line calculation type
Used in object.offerType (document total) and in object.selectedCalcType (document line). Valid values are the same numeric codes:
| Value | Meaning |
|---|---|
0 | Quantity-based offer |
1 | Percentage discount |
2 | Value discount |
3 | Points (amount) |
4 | Value voucher |
5 | Percentage voucher |
6 | Price |
7 | Quantity-based offer (new item) |
Campaign discount types
Value of discountType in each campaignItems element:
| Value | Meaning |
|---|---|
0 | Global discount |
1 | Fixed price |
2 | Line discount / price table |
Conditional Payload Dependencies
| Condition | What changes in JSON / semantics |
|---|---|
templateType | Defines the expected structure in object. Unknown value → code AdvancedDiscount.InvalidTemplateType. If the shape of object does not match templateType, AdvancedDiscount.InvalidObjectPayload may occur. |
object.eligibility (offers model, templateType 0) | Determines the type of IDs in eligibleIdList (item vs family). Must be 0 or 1 per eligibility table. |
object in offers | eligibleIdList must exist and have at least one element. offerAmount ≤ triggerAmount; both > 0. |
object in document total | amount and discount must be > 0 together. offerType must be a valid code in the Offer / line calculation types table. |
object in document line | discount > 0. eligibility and selectedCalcType must be defined values in the enums tables above. |
object in campaign | campaignItems mandatory and non-empty. Each item: id non-empty; discountType valid per Campaign discount types. |
startDate and endDate both filled | weekDaysAvailability cannot include a weekday that never occurs in the calendar range between the two dates (code AdvancedDiscount.WeekDaysOutsideDateRange). If the range covers 6 or more calendar days difference, all weekdays are allowed in the bitmask. |
includedTerminals | Always required: non-null and non-empty array with IDs existing in the database. |
Server Validations
General rules (before the object branch)
| Rule | Error Code |
|---|---|
startDate defined and date before today (comparison only on date part) | AdvancedDiscount.StartDateInPast |
startDate and endDate defined and startDate > endDate | AdvancedDiscount.InvalidDateRange |
appliedTo is not a known value (0 or 1) | AdvancedDiscount.InvalidAppliedTo |
templateType is not a known value (0–3) | AdvancedDiscount.InvalidTemplateType |
weekDaysAvailability is 0 or contains bits outside the seven days | AdvancedDiscount.InvalidWeekDaysAvailability |
With startDate and endDate, day bitmask includes day outside calendar range | AdvancedDiscount.WeekDaysOutsideDateRange |
includedTerminals null or empty | AdvancedDiscount.TerminalsRequired |
includedTerminals with non-existent IDs | AdvancedDiscount.InvalidTerminalIds |
Shape of object incompatible with templateType | AdvancedDiscount.InvalidObjectPayload |
By object type
| Context | Rule | Code |
|---|---|---|
| Offers | triggerAmount ≤ 0 | AdvancedDiscount.OffersTriggerAmountRequired |
| Offers | offerAmount ≤ 0 | AdvancedDiscount.OffersOfferAmountRequired |
| Offers | offerAmount > triggerAmount | AdvancedDiscount.OffersOfferExceedsTrigger |
| Offers | Invalid eligibility | AdvancedDiscount.InvalidEligibleType |
| Offers | eligibleIdList null or empty | AdvancedDiscount.OffersEligibleListRequired |
| Document total | amount ≤ 0 or discount ≤ 0 | AdvancedDiscount.DocTotalAmountAndDiscountRequired |
| Document total | Invalid offerType | AdvancedDiscount.InvalidOfferType |
| Document line | discount ≤ 0 | AdvancedDiscount.DocLineDiscountRequired |
| Document line | Invalid eligibility | AdvancedDiscount.InvalidEligibleType |
| Document line | Invalid selectedCalcType | AdvancedDiscount.InvalidSelectedCalcType |
| Campaign | campaignItems null or empty | AdvancedDiscount.CampaignItemsRequired |
| Campaign | Blank id in an item | AdvancedDiscount.CampaignItemIdRequired |
| Campaign | Invalid discountType | AdvancedDiscount.InvalidCampaignDiscountType |
Permissions and persistence
| Code | When |
|---|---|
AdvancedDiscount.PermissionDenied | User lacks permission to create, edit, or delete advanced discounts. |
AdvancedDiscount.NotFound | GET/PUT/DELETE with non-existent id. |
AdvancedDiscount.CreateFail | Failure to persist on creation. |
AdvancedDiscount.UpdateFail | Failure to persist on update. |
AdvancedDiscount.DeleteFail | Failure to delete. |
Listing validations (GET)
| Parameter | Behavior |
|---|---|
page | If omitted or < 1, page 1 is used. |
pageSize | If omitted or ≤ 0, 20 is used; otherwise, the request is applied up to a maximum of 100. |
JSON Examples by Scenario
Examples use camelCase and enum values as numbers. Adjust includedTerminals to terminals existing in the tenant, startDate to a date not before today (server validation), and weekDaysAvailability to a bitmask compatible with the startDate–endDate range when both are defined (see Conditional Payload Dependencies).
Offers — eligible items (templateType 0, eligibility 0)
{
"description": "Buy 5 units, get 2 free (eligible items)",
"active": true,
"includedTerminals": [1],
"discountOrder": 1,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 1,
"weekDaysAvailability": 127,
"templateType": 0,
"targetObject": null,
"object": {
"eligibility": 0,
"eligibleIdList": ["ART001", "ART002"],
"triggerAmount": 5,
"offerAmount": 2,
"selectedOfferItemKeyId": "ART-GIFT",
"offerItemMayNotBePresent": false
}
}
Offers — eligible families (templateType 0, eligibility 1)
{
"description": "Buy 10 pay 8 on selected families",
"active": true,
"includedTerminals": [1, 2],
"discountOrder": 2,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-09-30T23:59:59",
"appliedTo": 1,
"weekDaysAvailability": 127,
"templateType": 0,
"targetObject": null,
"object": {
"eligibility": 1,
"eligibleIdList": ["10", "20"],
"triggerAmount": 10,
"offerAmount": 2,
"selectedOfferItemKeyId": null,
"offerItemMayNotBePresent": true
}
}
Document total — percentage above threshold (templateType 1)
selectedCalcType 0 = “total above” (product semantics; the server mainly validates amount and discount > 0 and offerType).
{
"description": "10% discount on purchases above €100",
"active": true,
"includedTerminals": [1],
"discountOrder": 3,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 1,
"weekDaysAvailability": 127,
"templateType": 1,
"targetObject": null,
"object": {
"selectedCalcType": 0,
"amount": 100,
"offerType": 1,
"discount": 10,
"activeAfterNumberOfDays": 0,
"expiresAfterNumberOfDays": 0
}
}
Document total — fixed discount amount (templateType 1)
{
"description": "€15 discount above €200 total",
"active": true,
"includedTerminals": [1],
"discountOrder": 4,
"cumulative": true,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 1,
"weekDaysAvailability": 127,
"templateType": 1,
"targetObject": null,
"object": {
"selectedCalcType": 0,
"amount": 200,
"offerType": 2,
"discount": 15,
"activeAfterNumberOfDays": 0,
"expiresAfterNumberOfDays": 0
}
}
Document total — percentage voucher with validity in days (templateType 1)
offerType 5 = percentage voucher (see offer types table). The fields activeAfterNumberOfDays and expiresAfterNumberOfDays are part of the model; the server does not apply extra rules to these integers beyond the general object rules.
{
"description": "5% voucher valid after 7 days, expires after 30 days",
"active": true,
"includedTerminals": [1],
"discountOrder": 5,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 1,
"weekDaysAvailability": 127,
"templateType": 1,
"targetObject": null,
"object": {
"selectedCalcType": 0,
"amount": 50,
"offerType": 5,
"discount": 5,
"activeAfterNumberOfDays": 7,
"expiresAfterNumberOfDays": 30
}
}
Document line — percentage discount by family (templateType 2)
selectedCalcType must be a valid code in the Offer / line calculation types table (e.g., 1 = percentage discount).
{
"description": "5% on lines of electronics families",
"active": true,
"includedTerminals": [1],
"discountOrder": 6,
"cumulative": true,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 0,
"weekDaysAvailability": 31,
"templateType": 2,
"targetObject": null,
"object": {
"eligibility": 1,
"eligibleIdList": ["FAM001"],
"selectedCalcType": 1,
"discount": 5,
"activeAfterNumberOfDays": 0,
"expiresAfterNumberOfDays": 0
}
}
Document line — value discount by items (templateType 2)
{
"description": "€2 discount per line on key items",
"active": true,
"includedTerminals": [1],
"discountOrder": 7,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 0,
"weekDaysAvailability": 127,
"templateType": 2,
"targetObject": null,
"object": {
"eligibility": 0,
"eligibleIdList": ["SKU-A", "SKU-B"],
"selectedCalcType": 2,
"discount": 2,
"activeAfterNumberOfDays": 0,
"expiresAfterNumberOfDays": 0
}
}
Document line — targetObject with customer filter
{
"description": "Line discount only for customers from Lisbon",
"active": true,
"includedTerminals": [1],
"discountOrder": 8,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 0,
"weekDaysAvailability": 127,
"templateType": 2,
"targetObject": {
"logicOperator": "And",
"criterias": [
{
"variableName": "City",
"operation": "==",
"value": "Lisboa"
}
]
},
"object": {
"eligibility": 0,
"eligibleIdList": ["PROMO-LINE-01"],
"selectedCalcType": 1,
"discount": 3,
"activeAfterNumberOfDays": 0,
"expiresAfterNumberOfDays": 0
}
}
Campaign — multiple items and discount types (templateType 3)
{
"description": "Summer campaign — multiple lines",
"active": true,
"includedTerminals": [1],
"discountOrder": 9,
"cumulative": false,
"startDate": "2026-07-01T00:00:00",
"endDate": "2026-08-31T23:59:59",
"appliedTo": 0,
"weekDaysAvailability": 127,
"templateType": 3,
"targetObject": null,
"object": {
"campaignItems": [
{
"promoId": null,
"id": "ART100",
"type": 0,
"quantity": 2,
"percentage": 10,
"retailPriceHolder": 0,
"fixedPrice": 0,
"newPriceHolder": 0,
"descriptionHolder": null,
"afterQuantity": false,
"afterQuantityHolder": null,
"discountType": 0,
"priceLine": 1
},
{
"promoId": null,
"id": "ART200",
"type": 0,
"quantity": 1,
"percentage": 0,
"retailPriceHolder": 0,
"fixedPrice": 9.99,
"newPriceHolder": 0,
"descriptionHolder": null,
"afterQuantity": false,
"afterQuantityHolder": null,
"discountType": 1,
"priceLine": 1
},
{
"promoId": null,
"id": "GRP01",
"type": 1,
"quantity": 1,
"percentage": 0,
"retailPriceHolder": 0,
"fixedPrice": 0,
"newPriceHolder": 0,
"descriptionHolder": null,
"afterQuantity": false,
"afterQuantityHolder": null,
"discountType": 2,
"priceLine": 2
}
]
}
}
Update — body with id
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"description": "Updated rule",
"active": true,
"includedTerminals": [1],
"discountOrder": 10,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 1,
"weekDaysAvailability": 127,
"templateType": 0,
"targetObject": null,
"object": {
"eligibility": 0,
"eligibleIdList": ["ART001"],
"triggerAmount": 4,
"offerAmount": 1,
"selectedOfferItemKeyId": null,
"offerItemMayNotBePresent": false
}
}
For other combinations, consult the Create operation examples in the OpenAPI (Swagger) documentation exposed by the gateway.
Integration Notes
- Use
GET /gateway/advanced-discount/next-orderto suggestdiscountOrderin new rules. - On update, send the
idconsistent with the routePUT /gateway/advanced-discount/{id}. - Some additional error codes exist in the API internal list (e.g.,
DuplicateId,TemplateTypeRequired,ObjectRequired) but are not returned by the current validation and persistence flow of this resource; when in doubt, rely on the problem code returned in the response.
Related Resources
Last updated: May 14, 2026