Promotions
Manage of general promotions.
All JSON response field names in this API are camelCase, generated automatically from the underlying C# model — with a single deliberate exception,
APIRequireIssyTermsAcceptance/apiRequireIssyTermsAcceptance(kept as-is for backwards compatibility, casing differs by endpoint — see below). Request bodies accept PascalCase field names as shown (matching the API's internal model); this doc uses that same PascalCase for requests throughout for consistency with the rest of the site.
termsAndConditionsis 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'snullwhen 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 Promotions
Get list of available Promotions. The response is a bare JSON array (not wrapped in an envelope).
GET /promotions
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 - whether the API enforces T&C acceptance for this promotion. camelCase here (list endpoint) - see Terms & Conditions Acceptance in Get Started}",
"requireAgegateConfiguration": "{true/false, never null - whether the API enforces a minimum age on registration and participation for this promotion - 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}"
}
]
An empty array ([]) means the tenant has no available promotions — not an error.
Error responses:
| HTTP | errorCode | Cause |
|---|---|---|
| 404 | PROMOTION_NOT_FOUND | The search returned null (unexpected — normally an empty array is returned instead). |
| 401 / 406 | INVALID_HEADER_CONTENT | Missing/invalid issy-token or issy-tenant-id. |
| 500 | INTERNAL_ERROR | Unexpected server error. |
Get Promotion
Get an individual Promotion's full information, including its participation form and product list.
GET /promotions/{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). For a classic (non-loyalty) promotion this is only meaningful when requireProofOfPurchase is true, and is mutually exclusive with the invoice flow - photo participation isn't exposed as a one-step API endpoint for classic promotions yet. Loyalty programs behave differently - 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 - whether the API enforces T&C acceptance for this promotion. PascalCase here (detail endpoint, kept for backwards compatibility) - see Terms & Conditions Acceptance in Get Started}",
"requireAgegateConfiguration": "{true/false, never null - whether the API enforces a minimum age on registration and participation for this promotion - 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}",
"typeDescription": "{promotion type as text: Online, In Store}",
"prizeTypeDescription": "{promotion prize type as text, e.g. Enter to win, Instant Gratification, Time-Based Coupon, Top Ranking - useful as a badge/label}",
"activatePromotionWebButtonText": "{custom text for the promotion's activation button - null when not set, use your own default text}",
"onlineRedemptionType": "{0 = Single Code, 1 = Multicode, 2 = Pass Through, 3 = Cashback - null when not set}",
"onlineRedemtpionUrl": "{URL the user is sent to - for Pass Through (onlineRedemptionType 2) the activation button just opens this URL, there is no participation flow. Note: the field name is misspelled ('Redemtpion') in the API}",
"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}"
}
]
}
}
Note the casing difference: this endpoint returns
APIRequireIssyTermsAcceptance(PascalCase), while the list endpoint above returnsapiRequireIssyTermsAcceptance(camelCase) for the exact same field. This is intentional and applies the same way to Loyalty Programs and Rewards — see Terms & Conditions Acceptance.
Error responses:
| HTTP | errorCode | Cause |
|---|---|---|
| 404 | PROMOTION_NOT_FOUND | promotion id doesn't exist for this tenant. |
| 401 / 406 | INVALID_HEADER_CONTENT | Missing/invalid issy-token or issy-tenant-id. |
| 500 | INTERNAL_ERROR | Unexpected server error. |
error response example
{
"success": false,
"errorCode": "PROMOTION_NOT_FOUND",
"message": "Profile not found."
}
Get Promotion Locations
Get the locations where a promotion is available. Promotions, loyalty programs and rewards share the same underlying model, so this single endpoint works for all three — pass a promotion id, a loyalty program id or a reward id. The response is a bare JSON array (not wrapped in an envelope).
Only locations with status Available are returned (locations with a pending invitation, a failed/expired audit, or that reached their redemption amount are excluded). There is no pagination and no limit on the number of locations returned.
GET /promotions/{promotion id}/locations
RESPONSE (200)
[
{
"id": "{location id}",
"name": "{location name}",
"address": "{location address}",
"city": "{location city}",
"latitude": "{location coordinates latitude, number}",
"longitude": "{location coordinates longitude, number}"
}
]
An empty array ([]) means the promotion has no available locations — not an error.
Error responses:
| HTTP | errorCode | Cause |
|---|---|---|
| 404 | PROMOTION_NOT_FOUND | promotion id doesn't exist for this tenant. |
| 401 / 406 | INVALID_HEADER_CONTENT | Missing/invalid issy-token or issy-tenant-id. |
| 500 | INTERNAL_ERROR | Unexpected server error. |