Skip to main content

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

MethodURLBrief Description
GET/gateway/advanced-discountLists 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-orderReturns the next suggested order number.
POST/gateway/advanced-discountCreates 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-discountMethod: GET

See more →

Get by Identifier

URL: /gateway/advanced-discount/{id}Method: GET

See more →

Next Order

URL: /gateway/advanced-discount/next-orderMethod: GET

See more →

Create

URL: /gateway/advanced-discountMethod: POST

See more →

Update

URL: /gateway/advanced-discount/{id}Method: PUT

See more →

Delete

URL: /gateway/advanced-discount/{id}Method: DELETE

See more →


Request Body Structure

Property names follow camelCase in JSON. The discriminator templateType is mandatory; without it, the request is rejected during deserialization.

FieldTypeDescription
idstring (UUID)On update, existing identifier; on create, can be empty — the server generates a new identifier.
descriptionstringRequired.
activeboolActive flag.
includedTerminalsint[]Terminals where the rule applies; see validation Terminals.
discountOrderint?Application order.
cumulativeboolAllow stacking with other discounts.
startDatedate-time?Start of validity (date; see “past” validation).
endDatedate-time?End of validity.
appliedTointWhere the rule applies: line (0) or document (1); see table in Enums.
weekDaysAvailabilityint (bitmask)Days of the week when the rule is active; see Conditional Dependencies and value table.
targetObjectobject?Target customer filter criteria (nested structure with logical operators and criteria).
targetstring?Filled by the server from targetObject; not the focus of create/edit requests.
templateTypeintModel discriminator; see table below.
objectobjectModel-specific content; structure depends on templateType.

templateType and object shape

templateTypeModel
0Offers (buy X get Y free)
1Discount on document total
2Discount on document line
3Campaign (multiple promotion lines)

objectOffers model (templateType === 0)

FieldTypeDescription
eligibilityint0 = item, 1 = family — interprets the meaning of IDs in eligibleIdList.
eligibleIdListstring[]Eligible references (item codes or family identifiers, per eligibility).
triggerAmountdecimal“Per each” quantity (trigger).
offerAmountdecimalOffered quantity.
selectedOfferItemKeyIdstring?Offer item (optional).
offerItemMayNotBePresentboolWhether the offer item may not be present in the sale.

objectdocument total (templateType === 1)

FieldTypeDescription
selectedCalcTypeintCalculation 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.
amountdecimalThreshold / amount associated with the calculation mode.
offerTypeintOffer type on document total; must be a valid value according to the Offer / line calculation types table.
discountdecimalDiscount value (amount or percentage depending on offerType).
activeAfterNumberOfDaysintDays until voucher/discount becomes active (product semantics).
expiresAfterNumberOfDaysintValidity in days (product semantics).

objectdocument line (templateType === 2)

FieldTypeDescription
eligibilityintItem (0) or family (1) for eligibleIdList.
eligibleIdListstring[]List of references; the service does not require non-empty in this model, but the business engine may require it.
selectedCalcTypeintCalculation type on the line; must be a valid value according to the Offer / line calculation types table.
discountdecimalDiscount (effective value validated as > 0).
activeAfterNumberOfDaysintSame as the total model, at line scale.
expiresAfterNumberOfDaysintSame.

objectcampaign (templateType === 3)

FieldTypeDescription
campaignItemsarrayList of campaign lines; see properties in the following table.

campaignItems elements

FieldTypeNotes
promoIdint?Optional.
idstringCampaign line identifier; mandatory non-empty in server validation.
typeint0 = item, 1 = family / group.
percentage, retailPriceHolder, fixedPrice, newPriceHolder, quantity, priceLine, …numeric / stringFields supporting the campaign discount type.
discountTypeintCampaign discount type; mandatory and must be a value from the Campaign discount types table.
afterQuantity, afterQuantityHolder, descriptionHoldervariousAccording to campaign scenario.

Enums (numeric values in JSON)

templateType

ValueMeaning
0Offers (buy X get Y free)
1Discount on document total
2Discount on document line
3Campaign

appliedTo

ValueMeaning
0Line
1Document

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)

ValueDay
1Monday
2Tuesday
4Wednesday
8Thursday
16Friday
32Saturday
64Sunday

Combine with bitwise OR sum (e.g., weekdays = 31; all days = 127).

Eligibility in offers and line (eligibility)

ValueMeaning
0Item
1Family

Offer and line calculation type

Used in object.offerType (document total) and in object.selectedCalcType (document line). Valid values are the same numeric codes:

ValueMeaning
0Quantity-based offer
1Percentage discount
2Value discount
3Points (amount)
4Value voucher
5Percentage voucher
6Price
7Quantity-based offer (new item)

Campaign discount types

Value of discountType in each campaignItems element:

ValueMeaning
0Global discount
1Fixed price
2Line discount / price table

Conditional Payload Dependencies

ConditionWhat changes in JSON / semantics
templateTypeDefines 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 offerseligibleIdList must exist and have at least one element. offerAmounttriggerAmount; both > 0.
object in document totalamount and discount must be > 0 together. offerType must be a valid code in the Offer / line calculation types table.
object in document linediscount > 0. eligibility and selectedCalcType must be defined values in the enums tables above.
object in campaigncampaignItems mandatory and non-empty. Each item: id non-empty; discountType valid per Campaign discount types.
startDate and endDate both filledweekDaysAvailability 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.
includedTerminalsAlways required: non-null and non-empty array with IDs existing in the database.

Server Validations

General rules (before the object branch)

RuleError Code
startDate defined and date before today (comparison only on date part)AdvancedDiscount.StartDateInPast
startDate and endDate defined and startDate > endDateAdvancedDiscount.InvalidDateRange
appliedTo is not a known value (0 or 1)AdvancedDiscount.InvalidAppliedTo
templateType is not a known value (03)AdvancedDiscount.InvalidTemplateType
weekDaysAvailability is 0 or contains bits outside the seven daysAdvancedDiscount.InvalidWeekDaysAvailability
With startDate and endDate, day bitmask includes day outside calendar rangeAdvancedDiscount.WeekDaysOutsideDateRange
includedTerminals null or emptyAdvancedDiscount.TerminalsRequired
includedTerminals with non-existent IDsAdvancedDiscount.InvalidTerminalIds
Shape of object incompatible with templateTypeAdvancedDiscount.InvalidObjectPayload

By object type

ContextRuleCode
OfferstriggerAmount0AdvancedDiscount.OffersTriggerAmountRequired
OffersofferAmount0AdvancedDiscount.OffersOfferAmountRequired
OffersofferAmount > triggerAmountAdvancedDiscount.OffersOfferExceedsTrigger
OffersInvalid eligibilityAdvancedDiscount.InvalidEligibleType
OfferseligibleIdList null or emptyAdvancedDiscount.OffersEligibleListRequired
Document totalamount0 or discount0AdvancedDiscount.DocTotalAmountAndDiscountRequired
Document totalInvalid offerTypeAdvancedDiscount.InvalidOfferType
Document linediscount0AdvancedDiscount.DocLineDiscountRequired
Document lineInvalid eligibilityAdvancedDiscount.InvalidEligibleType
Document lineInvalid selectedCalcTypeAdvancedDiscount.InvalidSelectedCalcType
CampaigncampaignItems null or emptyAdvancedDiscount.CampaignItemsRequired
CampaignBlank id in an itemAdvancedDiscount.CampaignItemIdRequired
CampaignInvalid discountTypeAdvancedDiscount.InvalidCampaignDiscountType

Permissions and persistence

CodeWhen
AdvancedDiscount.PermissionDeniedUser lacks permission to create, edit, or delete advanced discounts.
AdvancedDiscount.NotFoundGET/PUT/DELETE with non-existent id.
AdvancedDiscount.CreateFailFailure to persist on creation.
AdvancedDiscount.UpdateFailFailure to persist on update.
AdvancedDiscount.DeleteFailFailure to delete.

Listing validations (GET)

ParameterBehavior
pageIf omitted or < 1, page 1 is used.
pageSizeIf 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 startDateendDate 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-order to suggest discountOrder in new rules.
  • On update, send the id consistent with the route PUT /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.


Last updated: May 14, 2026