POST/tenants/{tenantId}/transactions/redeem

The burn endpoint. Converts points to a rand discount at checkout. Returns discountAmount in cents — apply this as a line-item deduction at the till. Constraints like maxDiscountPercentOfBasketare applied automatically; constraintApplied tells you if they fired. Fully idempotent on transactionRef.

Preview before burning — use POST /transactions/redeem-quote to calculate the exact discount without writing any records. Ideal for "you would save R0.50" previews at checkout.

Path Parameters

tenantIdstring uuid
required
Your tenant UUID.

Request Body

transactionRefstring
required
Your unique reference for this redemption. Used for idempotency — safe to retry.
memberRefstring
required
Your customer's identifier. Must match an existing member.
amountinteger
required
Basket total in cents. Used to enforce maxDiscountPercentOfBasket limits. R150.00 = 15000.
pointsRequestedinteger
required
Points the member wants to redeem. Actual points redeemed may be less if a constraint applies.
campaignIdstring uuid
optional
Pin redemption to a specific campaign's burn rule. Omit to use the active default.
storeRefstring
optional
Store identifier. Required if redemption campaigns use store targeting.

Response 200

redeemTransactionIdstring uuid
required
Internal redemption transaction ID
transactionRefstring
required
Echoes your transactionRef
memberIdstring uuid
required
Internal member UUID
pointsRedeemedinteger
required
Actual points deducted (may be less than pointsRequested if a constraint fired)
discountAmountinteger
required
Discount to apply at checkout, in cents. R0.50 = 50.
balanceBeforeinteger
required
Member balance before this redemption
balanceAfterinteger
required
Member balance after this redemption
constraintAppliedstring | null
optional
Populated when a rule capped the redemption. Values: MAX_POINTS_PER_REDEMPTION · MAX_DISCOUNT_PERCENT_OF_BASKET · INSUFFICIENT_BALANCE

Discount formula

discountAmount = floor(pointsRedeemed / pointsPerRand)
// e.g. 500 pts ÷ 10 pts/R = R50 → 5000 cents

Error Codes

StatusCodeDescription
400VALIDATION_ERRORMissing or invalid field — check memberRef, amount, and pointsRequested
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENAPI key does not belong to this tenant
403MEMBER_SUSPENDEDMember account is suspended
404TENANT_NOT_FOUNDTenant ID does not exist
404MEMBER_NOT_FOUNDmemberRef does not match any member
404CAMPAIGN_NOT_FOUNDSpecified campaignId does not exist or has no redemption rule
409TRANSACTION_DUPLICATEtransactionRef already processed — idempotent result returned
422INSUFFICIENT_POINTSMember balance is lower than pointsRequested
cURL
curl -X POST "https://loyalty-engine-production-e5cb.up.railway.app/v1/tenants/$TENANT_ID/transactions/redeem" \
  -H "X-API-Key: sk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transactionRef":  "redeem_001",
    "memberRef":       "customer_27821234567",
    "amount":          15000,
    "pointsRequested": 500
  }'

Response

200 OK
{
  "redeemTransactionId": "a1b2c3d4-0e5f-4a8b-9f1d-2c3e4f5a6b7c",
  "transactionRef":      "redeem_001",
  "memberId":            "9f4c2e8a-1b2c-3d4e-5f6a-7b8c9d0e1f2a",
  "pointsRedeemed":      500,
  "discountAmount":      50,
  "balanceBefore":       650,
  "balanceAfter":        150,
  "constraintApplied":   null
}

Authenticate to test this endpoint live →

Loyalty Engine — Developer Portal