The Loyalty Engine returns errors as RFC 7807 application/problem+json responses. Every error carries a stable, machine-readable code alongside a human-readable detail — branch on code, show detail.
{
"type": "https://docs.yoyo.co.za/errors/INSUFFICIENT_POINTS",
"title": "Insufficient points",
"status": 422,
"detail": "Member balance (450) is lower than pointsRequested (500).",
"instance": "/tenants/.../transactions/redeem",
"code": "INSUFFICIENT_POINTS"
}status — HTTP status code, mirrored in the response status line.code — stable enum string. The contract you build against; never changes for a given condition.detail — a specific, human-readable explanation. Safe to surface in logs; phrasing may change.instance — the request path that produced the error.Retry-After header.transactionRef — idempotency guarantees no double-award.429 and 5xx, with exponential backoff. Because evaluate() and redeem() are idempotent on transactionRef, retrying is always safe. See Idempotency.