Skip to main content

Loyalty Programs

Manage of promotions with Loyalty Program type and rewards type.

All JSON response field names in this API are camelCase (generated automatically from the underlying C# model), with a single deliberate exception — APIRequireIssyTermsAcceptance/apiRequireIssyTermsAcceptance, casing differs by endpoint, see below. Request bodies use PascalCase (matching the API's internal model) as shown throughout this doc.

termsAndConditions is an HTML string, written with a rich-text editor in issy's backoffice — e.g. "<p>This is the space where the Terms and Conditions of the <b><u>Hennessy Bahamas Loyalty Program</u></b> will be displayed.</p>". Render it as sanitized HTML (e.g. DOMPurify), not as plain text. It's null when nothing is loaded. To build the full consent UI (privacy policy, brand opt-in, issy's legal pages) use Get Terms display — see Showing the consent UI.

Available Loyalty Programs​

Get list of available Loyalty Programs. The response is a bare JSON array (not wrapped in an envelope).

GET /loyaltyprograms
RESPONSE (200)
[
{
"id": "{promotion id}",
"tenantId": "{tenant id}",
"name": "{promotion name}",
"tags": "{array of strings - free-form labels assigned to the promotion in the backoffice (e.g. ["summer", "vip"]). Trimmed, case-insensitively de-duplicated, returned in the order they were saved. Always an array, [] when there are no tags}",
"description": "{promotion description}",
"code": "{promotion unique code}",
"enabled": "{promotion enabled}",
"mainImageUrl": "{promotion image url}",
"type": "{promotion type}",
"prizeType": "{promotion prize type}",
"requirePOPCode": "{whether participating requires a POP code - see Choosing a participation flow in Participations}",
"mechanicType": "{promotion mechanic type}",
"startDate": "{date when promotion starts}",
"endDate": "{date when promotion ends}",
"fraudPreventionModuleActivation": "{is active the fraud prevention module}",
"termsAndConditions": "{HTML string - promotion terms and conditions, see note below. Can be null}",
"policyPrivacyUrl": "{promotion privacy policy url}",
"apiRequireIssyTermsAcceptance": "{true/false, never null - see Terms & Conditions Acceptance in Get Started. camelCase here (list endpoint)}",
"requireAgegateConfiguration": "{true/false, never null - whether the API enforces a minimum age on registration and participation - see Age Gate in Get Started}",
"minimumAge": "{integer, never null - minimum age (in years) the user must have. 0 when not configured. Only enforced when requireAgegateConfiguration is true and minimumAge > 0}",
"endPromotionDesc": "{message to show when the participation process finishes}"
}
]

An empty array ([]) means the tenant has no available loyalty programs — not an error.

Error responses:

HTTPerrorCodeCause
404PROMOTION_NOT_FOUNDThe search returned null (unexpected — normally an empty array is returned instead).
401 / 406INVALID_HEADER_CONTENTMissing/invalid issy-token or issy-tenant-id.
500INTERNAL_ERRORUnexpected server error.

Get Loyalty Program​

Get an individual Loyalty Program's full information, including its participation form and product list.

GET /loyaltyprograms/{promotion id}
RESPONSE (200)
{
"id": "{promotion id}",
"tenantId": "{tenant id}",
"name": "{promotion name}",
"tags": "{array of strings - free-form labels assigned to the promotion in the backoffice (e.g. ["summer", "vip"]). Trimmed, case-insensitively de-duplicated, returned in the order they were saved. Always an array, [] when there are no tags}",
"description": "{promotion description}",
"code": "{promotion unique code}",
"enabled": "{promotion enabled}",
"mainImageUrl": "{promotion image url}",
"type": "{promotion type}",
"prizeType": "{promotion prize type}",
"requirePOPCode": "{whether participating requires a POP code - see Choosing a participation flow in Participations}",
"requireProofOfPurchase": "{whether participating requires an invoice/receipt image - only present in this detail response, not in the list above - see Choosing a participation flow in Participations}",
"proofOfPurchaseType": "{0 = invoice/receipt OCR, 1 = photo (AI rule evaluation) - independent of requireProofOfPurchase/requirePOPCode, a loyalty program can accept code, receipt and photo participation all at once - see Choosing a participation flow in Participations}",
"photoParticipationCredits": "{fixed credits granted per approved photo participation - only present when proofOfPurchaseType is 1}",
"mechanicType": "{promotion mechanic type}",
"startDate": "{date when promotion starts}",
"endDate": "{date when promotion ends}",
"fraudPreventionModuleActivation": "{is active the fraud prevention module}",
"termsAndConditions": "{HTML string - promotion terms and conditions, see note below. Can be null}",
"policyPrivacyUrl": "{promotion privacy policy url}",
"APIRequireIssyTermsAcceptance": "{true/false, never null - see Terms & Conditions Acceptance in Get Started. PascalCase here (detail endpoint, kept for backwards compatibility)}",
"requireAgegateConfiguration": "{true/false, never null - whether the API enforces a minimum age on registration and participation - see Age Gate in Get Started}",
"minimumAge": "{integer, never null - minimum age (in years) the user must have. 0 when not configured. Only enforced when requireAgegateConfiguration is true and minimumAge > 0}",
"endPromotionDesc": "{message to show when the participation process finishes ('End of Participation' message)}",
"officialReceiptName": "{title for the invoice/receipt capture screen ('Purchase Validation' title)}",
"photoLabel": "{title for the photo participation capture screen - used when proofOfPurchaseType is 1, null when not set}",
"rankingResetType": "{ranking window: 0 = None, 1 = Daily, 2 = Weekly, 3 = Monthly}",
"products": [
{
"product": {
"id": "{product id}",
"name": "{product name}"
}
}
],
"form": {
"formItems": [
{
"id": "{form item identifier}",
"statement": "{question statement}",
"imageUrl": "{image associated to question}",
"type": "{type of question: 0 => text, 1 => single-choice, 2 => multi-choice, 3 => image}",
"typeString": "{question type as text: Text, Single-Choice, Multi-Choice, Image}",
"fields": [
{
"id": "{field id}",
"name": "{name of field - not used for Text or Image questions}",
"imageUrl": "{final image url, used for consultation}",
"imageUrlBase64": "{image decoded in base64 - only for Image form items}",
"value": "{value of consumer answer}",
"hasPoints": "{whether the question accumulates points}",
"points": "{points of question}"
}
],
"totalPoints": "{maximum total possible points}",
"minimalPoints": "{minimum points to approve}"
}
]
}
}

Error responses:

HTTPerrorCodeCause
404PROMOTION_NOT_FOUNDpromotion id doesn't exist for this tenant.
401 / 406INVALID_HEADER_CONTENTMissing/invalid issy-token or issy-tenant-id.
500INTERNAL_ERRORUnexpected server error.

Available rewards of a Loyalty Program​

Get list of available rewards for a Loyalty Program. The response is a bare JSON array.

GET /rewards/{promotion id}
RESPONSE (200)
[
{
"id": "{reward id}",
"tenantId": "{tenant id}",
"name": "{reward name}",
"tags": "{array of strings - free-form labels assigned to the reward in the backoffice (e.g. ["summer", "vip"]). Trimmed, case-insensitively de-duplicated, returned in the order they were saved. Always an array, [] when there are no tags}",
"description": "{reward description}",
"termsAndConditions": "{HTML string - reward terms and conditions, see note below. Can be null}",
"endPromotionDesc": "{description to show when the redemption process finishes}",
"code": "{reward unique code}",
"enabled": "{reward enabled}",
"mainImageUrl": "{reward image url}",
"type": "{reward type}",
"prizeType": "{reward prize type}",
"requirePOPCode": "{whether redeeming requires a POP code}",
"mechanicType": "{reward mechanic type}",
"startDate": "{date when the reward becomes available}",
"endDate": "{date when the reward stops being available}",
"fraudPreventionModuleActivation": "{is active the fraud prevention module}",
"policyPrivacyUrl": "{reward privacy policy url}",
"apiRequireIssyTermsAcceptance": "{true/false, never null - see Terms & Conditions Acceptance in Get Started. camelCase here (list endpoint)}",
"requireAgegateConfiguration": "{true/false, never null - the reward's own value, always false/0 because the age gate can't be configured on a reward. Redemption is gated by the parent Loyalty Program's requireAgegateConfiguration/minimumAge (GET /loyaltyprograms/{id}) - see Age Gate in Get Started}",
"minimumAge": "{integer, never null - always 0 for a reward, see requireAgegateConfiguration above}",
"creditsToRedeem": "{total credits required to redeem the reward}",
"totalRedemptionLimit": "{max quantity of redemptions}",
"availableRedemptions": "{total redemptions still available for this reward}",
"prizeUrlCallToAction": "{label to show on the redemption code URL}",
"confirmationMessage": "{confirmation message to show when the user wants to redeem the reward}"
}
]

Error responses:

HTTPerrorCodeCause
404PROMOTION_NOT_FOUNDThe search returned null (unexpected — normally an empty array is returned instead).
401 / 406INVALID_HEADER_CONTENTMissing/invalid issy-token or issy-tenant-id.
500INTERNAL_ERRORUnexpected server error.

Get Reward of a Loyalty Program​

Get an individual reward's full information, including redemption locations.

GET /rewards/{promotion id}/{reward id}
RESPONSE (200)
{
"id": "{reward id}",
"tenantId": "{tenant id}",
"name": "{reward name}",
"tags": "{array of strings - free-form labels assigned to the reward in the backoffice (e.g. ["summer", "vip"]). Trimmed, case-insensitively de-duplicated, returned in the order they were saved. Always an array, [] when there are no tags}",
"description": "{reward description}",
"termsAndConditions": "{HTML string - reward terms and conditions, see note below. Can be null}",
"endPromotionDesc": "{description to show when the redemption process finishes}",
"code": "{reward unique code}",
"enabled": "{reward enabled}",
"mainImageUrl": "{reward image url}",
"type": "{reward type}",
"prizeType": "{reward prize type}",
"requirePOPCode": "{whether redeeming requires a POP code}",
"requireProofOfPurchase": "{whether redeeming requires an invoice/receipt image - only present in this detail response, not in the list above}",
"mechanicType": "{reward mechanic type}",
"startDate": "{date when the reward becomes available}",
"endDate": "{date when the reward stops being available}",
"fraudPreventionModuleActivation": "{is active the fraud prevention module}",
"policyPrivacyUrl": "{reward privacy policy url}",
"APIRequireIssyTermsAcceptance": "{true/false, never null - see Terms & Conditions Acceptance in Get Started. PascalCase here (detail endpoint, kept for backwards compatibility)}",
"requireAgegateConfiguration": "{true/false, never null - the reward's own value, always false/0 because the age gate can't be configured on a reward. Redemption is gated by the parent Loyalty Program's requireAgegateConfiguration/minimumAge (GET /loyaltyprograms/{id}) - see Age Gate in Get Started}",
"minimumAge": "{integer, never null - always 0 for a reward, see requireAgegateConfiguration above}",
"creditsToRedeem": "{total credits required to redeem the reward}",
"totalRedemptionLimit": "{max quantity of redemptions}",
"availableRedemptions": "{total redemptions still available for this reward}",
"confirmationMessage": "{confirmation message to show when the user wants to redeem the reward}",
"prizeUrlCallToAction": "{label to show on the redemption code URL}",
"locations": [
{
"location": {
"id": "{location id}",
"name": "{location name}",
"address": "{location address}",
"latitude": "{location coordinates latitude}",
"longitude": "{location coordinates longitude}"
}
}
]
}

locations in this response contains at most the 5 most recently modified locations of the reward, regardless of their status. To get the full list of available locations of a reward (or of a loyalty program), use Get Promotion Locations with the reward id.

Error responses:

HTTPerrorCodeCause
404LOYALTY_PROGRAM_NOT_FOUNDreward id doesn't exist, or doesn't belong to promotion id.
401 / 406INVALID_HEADER_CONTENTMissing/invalid issy-token or issy-tenant-id.
500INTERNAL_ERRORUnexpected server error.

Redeem reward​

Use user credits to redeem a reward.

POST /redeemreward
REQUEST
{
"PromotionId": "{Promotion id of the Loyalty Program the user is participating in}",
"UserId": "{user identifier}",
"SessionId": "{required only if the Loyalty Program is issy-managed - see User Session in Get Started}",
"Trafficsource": "{traffic source or UTM value}",
"RewardId": "{reward identifier}"
}
RESPONSE (200)
{
"redemptionId": "{Promotion location user participation Id created for this redemption}",
"codeType": "{QR or CODE}",
"redemptionCode": "{QR url or alphanumeric code}",
"redemptionUrl": "{URL to show the user, associated to the code}",
"confirmationMessage": "{Per-code banner message configured for this code (Multicode) or reward, when set - null otherwise}"
}

This endpoint doesn't run the Terms & Conditions gate (Get Started) — it's not affected by AcceptedTermsAndConditions/MarketingOptIn/BrandOwnerOptin.

It does run the Age Gate, evaluated against the requireAgegateConfiguration/minimumAge of the Loyalty Program the reward belongs to, using the user's stored birth date. It's checked before any credits are deducted or the redemption is created.

Error responses:

HTTPerrorCodeCause
401INVALID_PARAMSUserId doesn't exist, or has no participation profile for PromotionId.
401LOGIN_REQUIREDLoyalty Program is issy-managed and no SessionId was sent.
401INVALID_SESSIONSessionId is missing/expired, or belongs to a different UserId.
404PROMOTION_NOT_FOUNDPromotionId or RewardId doesn't exist.
406PROMOTION_INVALID_FOR_ACTIONRewardId doesn't point to a reward (IsReward is false).
406BIRTHDATE_REQUIREDThe reward's Loyalty Program has the age gate enabled and the user has no birth date stored.
406AGE_REQUIREMENT_NOT_METThe reward's Loyalty Program has the age gate enabled and the user is younger than its minimumAge.
406INSUFFICIENT_CREDITSNot enough credits available to cover the reward's cost.
451REDEMPTIONS_UNAVAILABLEThe reward's total redemption limit has been reached.
406INVALID_HEADER_CONTENTMissing/invalid issy-token or issy-tenant-id.
500REDEMPTION_CODE_UNAVAILABLENo code could be assigned for this reward (e.g. a Multicode pool with no Available codes left).
500INTERNAL_ERRORUnexpected server error.
error response example
{
"success": false,
"errorCode": "INSUFFICIENT_CREDITS",
"message": "Not credits available"
}

Transfer credits​

Executes the transfer of credits between two users, within the same promotion.

POST /transfercredits
REQUEST
{
"PromotionId": "{Promotion id}",
"SenderUserId": "{sender user identifier}",
"ReceiverUserId": "{receiver user identifier}",
"SessionId": "{required only if the Loyalty Program is issy-managed - must belong to SenderUserId, see User Session in Get Started}",
"Credits": "{Credits to transfer}"
}
RESPONSE (200)
{
"transferCreditsId": "{id of the credit transfer}"
}

This endpoint doesn't run the Terms & Conditions gate (Get Started).

Error responses:

HTTPerrorCodeCause
412INVALID_USERS_PARAMSSenderUserId and ReceiverUserId are the same.
401LOGIN_REQUIREDLoyalty Program is issy-managed and no SessionId was sent.
401INVALID_SESSIONSessionId is missing/expired, or doesn't belong to SenderUserId.
404USER_PROFILE_NOT_FOUNDSender or receiver has no participation profile for PromotionId.
404PROMOTION_NOT_FOUNDPromotionId doesn't exist for this tenant.
406INSUFFICIENT_CREDITSSender doesn't have enough credits available.
406INVALID_HEADER_CONTENTMissing/invalid issy-token or issy-tenant-id.
500INTERNAL_ERRORUnexpected server error.