Skip to content

Contract authority: OpenAPI is the machine-readable public v1 contract. This page is generated from the Privilege Integrator Pack. If prose and OpenAPI disagree, stop and contact Privilege.

5. Redemption flow ​

Public states (API): reserved → confirmed | cancelled, and confirmed → reversed.

text
validate  →  reserved
               ├─ confirm  →  confirmed
               └─ cancel   →  cancelled

confirmed  →  reverse  →  reversed
OperationCommercial meaning
validateCheck credential + create a short reservation/hold at this outlet
confirmSale succeeded; complete Privilege redemption (public state confirmed)
cancelSale did not complete; release the hold before confirm
reverseAfter confirm; void/refund path in Privilege terms — history is retained, not deleted

Do not call reverse on a reservation that was never confirmed — use cancel.


6. Validate ​

ItemContract
Method / pathPOST /redemptions/validate
Scoperedemptions:validate
HeadersAuthorization (Basic), Idempotency-Key (required)
Bodytoken (opaque Privilege token), externalOutletId

Success (200) means the token is usable now at the mapped outlet and a hold exists:

  • valid: true
  • redemptionId — use this UUID on confirm / cancel / reverse
  • privilegePromotionId — map to your local POS promotion (not auth, not pricing)
  • state: "reserved"
  • benefit: null
  • expiresAt — when the reservation ends (ISO-8601). Honor this; do not confirm after expiry.

Reservation length is server-configured (commonly on the order of a few minutes) and is clamped so it never exceeds the underlying credential/intent expiry. Always trust expiresAt from the response.

Common failures: TOKEN_INVALID, TOKEN_EXPIRED, OUTLET_UNMAPPED, OUTLET_DISABLED, MEMBERSHIP_INELIGIBLE, DEAL_NOT_REDEEMABLE, OFFER_UNSUPPORTED, auth/scope/rate-limit errors.

curl (placeholders only) ​

bash
curl -sS -X POST "${BASE_URL}/redemptions/validate" \
  -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ${IDEMPOTENCY_KEY}" \
  -H "X-Request-Id: pos-corr-001" \
  -d '{
    "token": "'"${PRIVILEGE_TOKEN}"'",
    "externalOutletId": "'"${EXTERNAL_OUTLET_ID}"'"
  }'

Example placeholders:

text
BASE_URL=https://dev.privilege.hr/api/integrations/v1
CLIENT_ID=pic_EXAMPLE_NOT_REAL
CLIENT_SECRET=pcs_EXAMPLE_NOT_REAL_DO_NOT_USE
EXTERNAL_OUTLET_ID=pos-outlet-001
PRIVILEGE_TOKEN=opaque_token_example_not_real_xxxxxxxx
IDEMPOTENCY_KEY=validate-pos-outlet-001-20260914-0001

Never log or echo the opaque token or clientSecret.


7. Confirm ​

Call after the POS has successfully applied its local promotion and completed its own sale.

ItemContract
Method / pathPOST /redemptions/{redemptionId}/confirm
Scoperedemptions:confirm
HeadersBasic auth, Idempotency-Key (required)
PathredemptionId from validate
Bodyrequired savings + receipt reference

Required body fields:

  • externalReceiptId (max 128) — opaque POS transaction reference
  • discountAmount — Privilege-attributable savings only (0 = genuine zero; omit is invalid)
  • currency — must be EUR

Optional:

  • externalOutletId — if present, must resolve to the reserved outlet
  • completedAt (ISO date-time)

Do not send grossAmount, finalAmount, or basket-total aliases — Privilege returns a validation error and does not persist them.

Canonical example:

text
POS applied local promo mapped from privilegePromotionId; Privilege savings = 4.50 EUR
→ externalReceiptId=<opaque>, discountAmount=4.50, currency=EUR
  • Send discountAmount: 0 → reported genuine zero.
  • Omit discountAmount → rejected.

Success (200): state: "confirmed", privilegePromotionId, benefit: null, privilegeRedemptionId, savings, savingsSource: "reported", savingsAttribution: "privilege_only_contract".

Idempotent replay of the same key + same request returns the same success without creating a second commercial redemption.

curl ​

bash
curl -sS -X POST "${BASE_URL}/redemptions/${REDEMPTION_ID}/confirm" \
  -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ${IDEMPOTENCY_KEY}" \
  -d '{
    "externalOutletId": "'"${EXTERNAL_OUTLET_ID}"'",
    "externalReceiptId": "POS-RCPT-0001",
    "discountAmount": 15,
    "currency": "EUR",
    "completedAt": "2026-09-14T12:01:30.000Z"
  }'

8. Cancel ​

Use when checkout is abandoned or fails after validate and before confirm (including unknown local promo mapping — apply no benefit, then cancel).

ItemContract
Method / pathPOST /redemptions/{redemptionId}/cancel
Scoperedemptions:cancel
HeadersBasic auth, Idempotency-Key (required)
Bodyoptional reason, optional externalOutletId

Success (200): state: "cancelled".

Confirmed redemptions cannot be cancelled — use reverse.

curl ​

bash
curl -sS -X POST "${BASE_URL}/redemptions/${REDEMPTION_ID}/cancel" \
  -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ${IDEMPOTENCY_KEY}" \
  -d '{
    "reason": "operator_abort",
    "externalOutletId": "'"${EXTERNAL_OUTLET_ID}"'"
  }'

9. Reverse ​

Use after a successful confirm when the commercial outcome must be reversed in Privilege (e.g. void / return), to the extent your POS supports that path.

ItemContract
Method / pathPOST /redemptions/{redemptionId}/reverse
Scoperedemptions:reverse
HeadersBasic auth, Idempotency-Key (required)
Bodyrequired: at least one of reason or reasonCategory (non-empty; max 256 combined in practice)

Success (200): state: "reversed", optional privilegeRedemptionId. The commercial history row is retained (not deleted).

Commercial reverse time-window — UNRESOLVED ​

v1 does not publish or enforce a commercial reverse deadline. Integrators must not assume unlimited reversal forever. Final operational policy may be defined separately by Privilege.

Partial refunds — not supported by this API ​

reverse is a full commercial reversal of the Privilege redemption. It is not a partial-refund API. Privilege does not document an automatic “full reverse then re-redeem” workaround (that path can affect tokens, eligibility limits, and accounting). If your POS needs partial-refund reconciliation with Privilege, escalate during onboarding — a pilot that requires partial Privilege refunds is not ready until a supported resolution exists.

curl ​

bash
curl -sS -X POST "${BASE_URL}/redemptions/${REDEMPTION_ID}/reverse" \
  -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ${IDEMPOTENCY_KEY}" \
  -d '{
    "reasonCategory": "storno",
    "reason": "customer_return"
  }'

Privilege Developers — public External Integration documentation. OpenAPI is the machine-readable contract.