Errors

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.

Error shape

application/problem+json
{
  "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.

Status code semantics

  • 4xx — your request needs fixing. Do not blindly retry; correct the input first.
  • 429 — back off and retry after the Retry-After header.
  • 5xx — transient server error. Retry with the same transactionRef — idempotency guarantees no double-award.

Error codes

StatuscodeRetryWhen
400VALIDATION_ERRORNoMissing or invalid field. The detail names the offending field.
401UNAUTHORIZEDNoMissing or invalid API key.
403FORBIDDENNoAPI key does not belong to this tenant.
403MEMBER_SUSPENDEDNoMember account is suspended and cannot transact.
404TENANT_NOT_FOUNDNoTenant ID does not exist.
404MEMBER_NOT_FOUNDNomemberRef does not match any member (redeem only — evaluate auto-creates).
404CAMPAIGN_NOT_FOUNDNoSpecified campaignId does not exist or has no matching rule.
409TRANSACTION_DUPLICATENotransactionRef already processed — the original result is returned, not an error in most cases.
422INSUFFICIENT_POINTSNoMember balance is lower than pointsRequested.
429RATE_LIMITEDYesRate limit exceeded. Honour the Retry-After header.
500INTERNAL_ERRORYesUnexpected server error. Safe to retry with the same transactionRef.
Idempotency & retries: Only retry on 429 and 5xx, with exponential backoff. Because evaluate() and redeem() are idempotent on transactionRef, retrying is always safe. See Idempotency.
Errors — Loyalty Engine Docs