Skip to main content

Participations

Participations are the interaction of users with experiences. A Participation is created as soon as the consumer initiates the participation process.

Choosing a participation flow​

There are now five ways to create a participation. Check these fields on the promotion (from Get Promotion / Get Loyalty Program, detail endpoints only) to know which flows are available for it:

FieldMeans the promotion accepts...Use
requireProofOfPurchase (and proofOfPurchaseType is not 1)an invoice/receipt imagebeginparticipationwithpop → saveparticipation — two steps
requirePOPCodea printed/pack POP code — loyalty programs onlycreateparticipationwithcode — one step
proofOfPurchaseType === 1a photo as proof of purchase — loyalty programs onlycreateparticipationwithphoto — one step
none of the aboveno proof at allcreateparticipation — one step

As of 2026-08 these are independent, not mutually exclusive — a loyalty program can have code, receipt, and photo participation all enabled at once, letting the member choose how they want to participate each time. For a classic (non-loyalty) promotion, requireProofOfPurchase and requirePOPCode are still never both true at once, and proofOfPurchaseType: 1 (photo) is only meaningful when requireProofOfPurchase is also true (it's the classic promotion's own invoice-vs-photo choice, not a fourth independent flag — photo participation for classic promotions isn't exposed as a one-step endpoint yet).

Whichever flows are enabled, call the one that matches the input the user actually has in hand right now — don't mix inputs from different methods into the same call (e.g. don't send a PopCode to createparticipationwithphoto).

There are two unrelated "code" concepts in this API — don't confuse them:

  • POP code (PopCode field, this section): a printed/pack code redeemed for participation credits. Validated against IssyPromosCodes.POPCodes.
  • Traffic source / UTM (Trafficsource field, every participation endpoint): where the user came from, unrelated to POP codes.

POST beginparticipation (without proof of purchase, two-step) still exists but is obsolete — see its section below. Prefer createparticipation.

For an issy-managed loyalty program (isLoyaltyProgram: true), all flows above additionally require UserId to be the account id from Account Authentication plus a valid SessionId — see User Session in Get Started. Client-managed promotions are unaffected.

Create participation (one step, no invoice or POP code)​

Preferred one-step flow for promotions that require neither an invoice nor a POP code. Creates the user (if needed), enforces Terms & Conditions, saves optional form answers, and activates the participation — all in a single call.

POST /createparticipation
REQUEST
{
"PromotionId": "{Promotion id}",
"UserId": "{user identifier}",
"SessionId": "{required only for an issy-managed loyalty program - see User Session in Get Started}",
"IsPhoneNumber": false,
"Trafficsource": "{traffic source or UTM value}",
"AcceptedTermsAndConditions": true,
"MarketingOptIn": true,
"BrandOwnerOptin": true,
"Answers": [
{
"Field": {
"Id": "{field identifier}",
"value": "{value of consumer answer}"
}
}
]
}

IsPhoneNumber matters only if UserId doesn't match an existing user yet — it tells the API to create the new user by phone instead of by email. AcceptedTermsAndConditions/MarketingOptIn/BrandOwnerOptin only apply when this promotion is issy-managed — see Terms & Conditions Acceptance. Answers follows the same rules as Complete participation process below (required only if the promotion has a form).

RESPONSE (200) - no reward code generated yet
{
"promotionLocationUserId": "{Promotion location user participation Id}"
}
RESPONSE (200) - an instant-win/IG redemption code was generated
{
"promotionLocationUserId": "{Promotion location user participation Id}",
"codeType": "{QR or CODE}",
"redemptionCode": "{QR url or alphanumeric code}",
"redemptionUrl": "{URL to show the user, associated to the code}"
}

Do not call saveparticipation after this — the participation is already complete.

Error responses:

HTTPerrorCodeCause
404PROMOTION_NOT_FOUNDPromotionId doesn't exist for this tenant.
406PROMOTION_INVALID_FOR_ACTIONThe promotion requires an invoice, a POP code, or a photo — use one of the other flows instead.
401LOGIN_REQUIREDLoyalty program is issy-managed and no SessionId was sent.
401INVALID_SESSIONSessionId is missing/expired, or belongs to a different UserId.
412TERMS_NOT_ACCEPTEDPromotion is issy-managed and required acceptance is missing — see requiredAcceptances.
400TERMS_FIELD_NOT_APPLICABLESent BrandOwnerOptin/MarketingOptIn for the wrong tenant mode.
406BIRTHDATE_REQUIREDThe promotion has the Age Gate enabled and the user has no birth date stored.
406AGE_REQUIREMENT_NOT_METThe promotion has the Age Gate enabled and the user is younger than minimumAge.
404PROMOTION_FORM_NOT_FOUNDAnswers were sent but the promotion has no form configured.
404FORM_ITEM_NOT_FOUNDAn answered Field.Id doesn't belong to the promotion's form.
412INVALID_PARTICIPATION_ANSWERSFewer answers than the promotion's configured minimum.
406INVALID_HEADER_CONTENTMissing/invalid issy-token or issy-tenant-id.
500INTERNAL_ERRORUnexpected server error.
error response example
{
"success": false,
"errorCode": "PROMOTION_INVALID_FOR_ACTION",
"message": "Promotion invalid for this action."
}

Create loyalty participation with POP code (one step)​

One-step flow for loyalty programs (isLoyaltyProgram: true) that require a POP code (requirePOPCode: true). This path ignores any invoice/image — don't send one, and don't reuse a previous proof of purchase.

POST /createparticipationwithcode
REQUEST
{
"PromotionId": "{Promotion id}",
"UserId": "{user identifier}",
"SessionId": "{required only if the loyalty program is issy-managed - see User Session in Get Started}",
"IsPhoneNumber": false,
"PopCode": "{printed/pack POP code}",
"Trafficsource": "{traffic source or UTM value}",
"AcceptedTermsAndConditions": true,
"MarketingOptIn": true,
"BrandOwnerOptin": true,
"Answers": [
{
"Field": {
"Id": "{field identifier}",
"value": "{value of consumer answer}"
}
}
]
}

Behavior:

  • A valid, available code with credits assigned (Credits not null — 0 is a valid amount) → the participation is auto-Approved and those credits are granted immediately; the code is marked used.
  • If the user/participation is flagged (blacklist, suspicious activity) or a redemption limit is hit → the participation is created as PendingReview, with no credits granted (same as manual review elsewhere in the API) — this is not an error, check the status field in the response.
RESPONSE (200)
{
"promotionLocationUserId": "{Promotion location user participation Id}",
"status": "{Approved, PendingReview, or Invalid, as a string}",
"creditsAssigned": "{Credits granted by this call - 0 if status isn't Approved}",
"creditsAvailable": "{User's total credits available for this promotion after this call}"
}

Do not call saveparticipation after this — that endpoint doesn't auto-approve loyalty participations and would leave this one stuck in PendingReview, undoing the auto-approval.

Error responses:

HTTPerrorCodeCause
404PROMOTION_NOT_FOUNDPromotionId doesn't exist for this tenant.
406PROMOTION_INVALID_FOR_ACTIONThe promotion isn't a loyalty program with requirePOPCode: true.
406POP_CODE_REQUIREDPopCode is empty.
406POP_CODE_INVALIDPopCode doesn't exist for this promotion, or was already used.
406POP_CODE_CREDITS_MISSINGThe code exists but has no Credits configured.
401LOGIN_REQUIREDLoyalty program is issy-managed and no SessionId was sent.
401INVALID_SESSIONSessionId is missing/expired, or belongs to a different UserId.
412TERMS_NOT_ACCEPTEDPromotion is issy-managed and required acceptance is missing.
400TERMS_FIELD_NOT_APPLICABLESent BrandOwnerOptin/MarketingOptIn for the wrong tenant mode.
406BIRTHDATE_REQUIREDThe promotion has the Age Gate enabled and the user has no birth date stored.
406AGE_REQUIREMENT_NOT_METThe promotion has the Age Gate enabled and the user is younger than minimumAge.
404PROMOTION_FORM_NOT_FOUNDAnswers were sent but the promotion has no form configured.
404FORM_ITEM_NOT_FOUNDAn answered Field.Id doesn't belong to the promotion's form.
412INVALID_PARTICIPATION_ANSWERSFewer answers than the promotion's configured minimum.
406INVALID_HEADER_CONTENTMissing/invalid issy-token or issy-tenant-id.
500INTERNAL_ERRORUnexpected server error.
error response example
{
"success": false,
"errorCode": "POP_CODE_INVALID",
"message": "POP code is invalid or already used."
}

Create loyalty participation with photo (one step)​

One-step flow for loyalty programs (isLoyaltyProgram: true) with photo-based proof of purchase enabled (proofOfPurchaseType: 1) — independent of requirePOPCode/requireProofOfPurchase, which may or may not also be enabled on the same program. The photo is evaluated synchronously, in the same call, against the program's configured evaluation rules (an AI vision check, not OCR) — this is a different mechanism from the invoice/receipt flow below, and cannot be used for a program that only has requireProofOfPurchase (invoice) enabled.

POST /createparticipationwithphoto
REQUEST
{
"PromotionId": "{Promotion id}",
"UserId": "{user identifier}",
"SessionId": "{required only if the loyalty program is issy-managed - see User Session in Get Started}",
"IsPhoneNumber": false,
"ImageExtension": "{Extension of image file, e.g. jpg, png}",
"ImageBase64": "{Photo encoded to Base64}",
"Trafficsource": "{traffic source or UTM value}",
"AcceptedTermsAndConditions": true,
"MarketingOptIn": true,
"BrandOwnerOptin": true,
"Answers": [
{
"Field": {
"Id": "{field identifier}",
"value": "{value of consumer answer}"
}
}
]
}

Behavior:

  • The photo is scored against the program's evaluation rules. If the program's approval mode is Automatic and the match percentage reaches the configured threshold → the participation is auto-Approved and a fixed number of credits (photoParticipationCredits on the loyalty program) is granted immediately, in this same call — unlike the invoice flow, a loyalty photo participation does not get force-held for manual review just for being a loyalty program.
  • If the approval mode is Data Quality Validation, or the photo doesn't reach the threshold → the participation is created as PendingReview; credits are granted later if/when a reviewer approves it manually (same review queue as everything else).
  • If the same photo (by checksum) was already used in a previous participation on this promotion → rejected with PHOTO_DUPLICATED (see below), the participation is not created as Approved.
RESPONSE (200)
{
"promotionLocationUserId": "{Promotion location user participation Id}",
"status": "{Approved or PendingReview, as a string}",
"photoResult": {
"percentaje": "{match percentage 0-100 the AI evaluation scored this photo}",
"matchedRules": ["{descriptions of the evaluation rules this photo satisfied}"],
"missingRules": ["{descriptions of the evaluation rules this photo did NOT satisfy}"],
"observation": "{free-text note from the evaluation, may be null}"
},
"creditsAssigned": "{Credits granted by this call - 0 if status isn't Approved}",
"creditsAvailable": "{User's total credits available for this promotion after this call}"
}

Do not call saveparticipation after this — the participation is already complete, whether Approved or PendingReview.

Error responses:

HTTPerrorCodeCause
404PROMOTION_NOT_FOUNDPromotionId doesn't exist for this tenant.
406PROMOTION_INVALID_FOR_ACTIONThe promotion isn't a loyalty program with proofOfPurchaseType: 1.
406IMAGE_REQUIREDImageBase64/ImageExtension is missing.
400PHOTO_VALIDATION_FAILEDThe photo couldn't be evaluated (e.g. unreadable image).
409PHOTO_DUPLICATEDThis exact photo was already used in a previous participation on this promotion.
401LOGIN_REQUIREDLoyalty program is issy-managed and no SessionId was sent.
401INVALID_SESSIONSessionId is missing/expired, or belongs to a different UserId.
412TERMS_NOT_ACCEPTEDPromotion is issy-managed and required acceptance is missing.
400TERMS_FIELD_NOT_APPLICABLESent BrandOwnerOptin/MarketingOptIn for the wrong tenant mode.
406BIRTHDATE_REQUIREDThe promotion has the Age Gate enabled and the user has no birth date stored.
406AGE_REQUIREMENT_NOT_METThe promotion has the Age Gate enabled and the user is younger than minimumAge.
404PROMOTION_FORM_NOT_FOUNDAnswers were sent but the promotion has no form configured.
404FORM_ITEM_NOT_FOUNDAn answered Field.Id doesn't belong to the promotion's form.
412INVALID_PARTICIPATION_ANSWERSFewer answers than the promotion's configured minimum.
406INVALID_HEADER_CONTENTMissing/invalid issy-token or issy-tenant-id.
500INTERNAL_ERRORUnexpected server error.
error response example
{
"success": false,
"errorCode": "PHOTO_DUPLICATED",
"message": "This photo was already used in a previous participation."
}

Begin participation process WITH proof of purchase​

First step of the two-step invoice flow — for promotions where requireProofOfPurchase is true (classic promotions, and loyalty programs that review invoices instead of POP codes). Unchanged by the one-step endpoints above.

POST /beginparticipationwithpop

POST /validatepopformat is this flow's predecessor and is deprecated — use beginparticipationwithpop for new integrations. (Despite the legacy route name, it's about the invoice/proof-of-purchase path, not the newer PopCode field below.)

REQUEST
{
"PromotionId": "{Promotion id}",
"UserId": "{user identifier}",
"SessionId": "{required only if this is an issy-managed loyalty program - see User Session in Get Started}",
"Trafficsource": "{traffic source or UTM value}",
"IsPhoneNumber": false,
"ImageExtension": "{Extension of image file, e.g. jpg, png}",
"ImageBase64": "{Receipt image encoded to Base64}",
"AcceptedTermsAndConditions": true,
"MarketingOptIn": true,
"BrandOwnerOptin": true
}
RESPONSE (200)
{
"promotionLocationUserId": "{Promotion location user participation Id, use it in Complete participation process}"
}

The Terms & Conditions gate (TERMS_NOT_ACCEPTED / TERMS_FIELD_NOT_APPLICABLE) and the Age Gate (BIRTHDATE_REQUIRED / AGE_REQUIREMENT_NOT_MET) are checked before the receipt image is evaluated (or stored). If that passes, the receipt is validated and can fail with a range of POP_VALIDATION_FAILED outcomes, distinguished by HTTP status:

HTTPerrorCodemessageCause
300POP_VALIDATION_FAILEDpop_duplicatedThis receipt/invoice was already used.
406POP_VALIDATION_FAILEDnot_recognizedThe image couldn't be read as a valid receipt.
451POP_VALIDATION_FAILEDempty_invoice_fieldRequired receipt fields (e.g. invoice number) came back empty.
500POP_VALIDATION_FAILED{internal exception message}The recognition service failed unexpectedly.
POP validation error example
{
"success": false,
"errorCode": "POP_VALIDATION_FAILED",
"message": "pop_duplicated"
}

Other error responses:

HTTPerrorCodeCause
404PROMOTION_NOT_FOUNDPromotionId doesn't exist for this tenant.
401LOGIN_REQUIREDLoyalty program is issy-managed and no SessionId was sent.
401INVALID_SESSIONSessionId is missing/expired, or belongs to a different UserId.
412TERMS_NOT_ACCEPTEDPromotion is issy-managed and required acceptance is missing.
400TERMS_FIELD_NOT_APPLICABLESent BrandOwnerOptin/MarketingOptIn for the wrong tenant mode.
406BIRTHDATE_REQUIREDThe promotion has the Age Gate enabled and the user has no birth date stored.
406AGE_REQUIREMENT_NOT_METThe promotion has the Age Gate enabled and the user is younger than minimumAge.
406INVALID_HEADER_CONTENTMissing/invalid issy-token or issy-tenant-id.
500INTERNAL_ERRORUnexpected server error (outside of POP validation itself).

For loyalty programs, this path always leaves the participation in PendingReview for manual review — it never auto-approves like createparticipationwithcode does.

Complete participation process​

Second step of the two-step flows above (beginparticipationwithpop, and the obsolete beginparticipation) — confirms the participation by sending the responses to the participation form answers (optional, depending on the promotion's configuration) and activates it.

Not used after createparticipation, createparticipationwithcode, or createparticipationwithphoto — all three already activate the participation in the same call.

"Answers": The promotion configuration is what determines if it is required to send this attribute. The queried object of a promotion has an attribute called Form. If this attribute is not null, you must send the Answers attribute to complete the participation. The Form attribute has a FormItems array that references each question configured in the promotion for participations. The Answers attribute is an array of objects that contains the user's responses:

  • PromotionLocationUserId: returned by the begin participation endpoint.
  • Field: refers to the FormItem field of the question that the user answered.
    • Id: FieldId
    • value: Value of consumer answer
POST /saveparticipation
REQUEST
{
"PromotionId": "{Promotion id}",
"UserId": "{user identifier}",
"SessionId": "{required only if this is an issy-managed loyalty program - see User Session in Get Started}",
"Trafficsource": "{traffic source or UTM value}",
"PromotionLocationUserId": "{PromotionLocationUserId returned by the begin participation endpoint}",
"AcceptedTermsAndConditions": true,
"MarketingOptIn": true,
"BrandOwnerOptin": true,
"Answers": [
{
"PromotionLocationUserId": "{PromotionLocationUserId returned by the begin participation endpoint}",
"Field": {
"Id": "{field identifier}",
"value": "{Value of consumer answer}"
}
}
]
}
RESPONSE (200) - no reward code generated yet
{
"promotionLocationUserId": "{Promotion location user participation Id}"
}
RESPONSE (200) - a redemption code was generated for this participation
{
"promotionLocationUserId": "{Promotion location user participation Id}",
"codeType": "{QR or CODE}",
"redemptionCode": "{QR url or alphanumeric code}",
"redemptionUrl": "{URL to show the user, associated to the code}"
}

Whether the second shape is returned depends entirely on the promotion's mechanic (e.g. instant-win promotions that generate a code immediately vs. promotions that require manual review first) — always check for redemptionCode being present rather than assuming one shape. For loyalty participations completed here, the result typically stays PendingReview (pending manual invoice review) — instant credit approval only happens via createparticipationwithcode or, when the automatic photo match succeeds, createparticipationwithphoto.

Error responses:

HTTPerrorCodeCause
404USER_NOT_FOUNDUserId doesn't match a registered user.
404PROMOTION_NOT_FOUNDPromotionId doesn't exist for this tenant.
401LOGIN_REQUIREDLoyalty program is issy-managed and no SessionId was sent.
401INVALID_SESSIONSessionId is missing/expired, or belongs to a different UserId.
412TERMS_NOT_ACCEPTEDPromotion is issy-managed and required acceptance is missing.
400TERMS_FIELD_NOT_APPLICABLESent BrandOwnerOptin/MarketingOptIn for the wrong tenant mode.
406BIRTHDATE_REQUIREDThe promotion has the Age Gate enabled and the user has no birth date stored.
406AGE_REQUIREMENT_NOT_METThe promotion has the Age Gate enabled and the user is younger than minimumAge.
404PARTICIPATION_NOT_FOUNDPromotionLocationUserId doesn't match an existing participation.
412INVALID_PARTICIPATIONThe participation doesn't belong to this UserId, or was already completed.
404PROMOTION_FORM_NOT_FOUNDAnswers were sent but the promotion has no form configured.
404FORM_ITEM_NOT_FOUNDAn answered Field.Id doesn't belong to the promotion's form.
412INVALID_PARTICIPATION_ANSWERSFewer answers than the promotion's configured minimum.
406INVALID_HEADER_CONTENTMissing/invalid issy-token or issy-tenant-id.
500INTERNAL_ERRORUnexpected server error.
error response example
{
"success": false,
"errorCode": "INVALID_PARTICIPATION",
"message": "Invalid participation."
}

Begin participation process WITHOUT proof of purchase (obsolete)​

Obsolete — use createparticipation instead

This endpoint still works (existing integrations keep functioning) but is no longer the recommended path for new integrations. The successful response includes the header Deprecation: true. It only creates an incomplete participation — you must still call saveparticipation afterwards, unlike createparticipation which does both in one call.

POST /beginparticipation
REQUEST
{
"PromotionId": "{Promotion id}",
"UserId": "{user identifier}",
"SessionId": "{required only if this is an issy-managed loyalty program - see User Session in Get Started}",
"Trafficsource": "{traffic source or UTM value}",
"IsPhoneNumber": false,
"AcceptedTermsAndConditions": true,
"MarketingOptIn": true,
"BrandOwnerOptin": true
}
RESPONSE (200)
Headers: Deprecation: true
{
"promotionLocationUserId": "{Promotion location user participation Id, use it in Complete participation process}"
}

Error responses: same table as Create participation above, except it only checks requireProofOfPurchase (not requirePOPCode) and it never processes Answers (that happens in the follow-up saveparticipation call instead).