# Redemption Redeem — Error Reference

This page lists every error response your application can receive when redeeming a reward
(`POST /redemptions/redeems`) and when reading redemption-redeem status
(`GET /redemptions/redeems/me`, `GET /redemptions/redeems`, `GET /redemptions/redeems/{redeemId}`).

For the general error envelope, categories, and client-handling patterns, see the
[Error Handling guide](/8.error-handling) — this page only covers what's specific to redeeming.

## Response format

Every error is a JSON object with an HTTP status code matching the `status` field:

```json
{
  "status": 409,
  "title": "You have a redemption currently being processed (ID: 6e2c..., status: PROCESSING). Please wait for it to complete.",
  "detail": "You have a redemption currently being processed (ID: 6e2c..., status: PROCESSING). Please wait for it to complete.",
  "message": "You have a redemption currently being processed (ID: 6e2c..., status: PROCESSING). Please wait for it to complete.",
  "code": "REDEMPTION_ALREADY_PROCESSING",
  "category": "DOMAIN_RULE",
  "retryable": false,
  "domain": "business",
  "timestamp": "2026-08-24T10:00:00.000Z"
}
```

Use `code` for programmatic handling — don't parse `message`/`title`/`detail`, their wording can
change. `retryable: true` means the same request is safe to retry unmodified after a short delay;
`retryable: false` means the request needs to change before it can succeed (or the situation needs
to resolve on its own, e.g. waiting for an in-progress redemption to finish).

## Using the `@explorins/pers-sdk`

If you're on the [PERS SDK](https://docs.pers.ninja/sdk-intro) rather than calling the REST API
directly, redemption calls go through `sdk.redemptions.*` and surface the same error codes wrapped
in the SDK's own error classes ([SDK Error Handling](https://docs.pers.ninja/sdk-intro#error-handling)):

```typescript
import { PersApiError, AuthenticationError } from "@explorins/pers-sdk/core";

try {
  const result = await sdk.redemptions.redeem({ redemptionId: "redemption-123" });
} catch (error) {
  if (error instanceof AuthenticationError) {
    // 401 — redirect to login
  } else if (error instanceof PersApiError) {
    // The SDK passes the full backend structured error through onto the thrown
    // instance — not just status/message: error.code, error.category, error.domain,
    // error.retryable, and error.details all map 1:1 to the tables below.
    console.error("Redemption failed:", error.code, error.status, error.category, error.retryable);
  }
}
```

## `POST /redemptions/redeems` — Execute a redemption

| HTTP | `code` | Retryable | What it means | What to do |
|  --- | --- | --- | --- | --- |
| 401 | `AUTHENTICATION_REQUIRED` | No | You're not signed in, or your session doesn't identify a user. | Sign the user in and retry. |
| 401 | `INVALID_TOKEN` / `TOKEN_EXPIRED` / `TOKEN_REVOKED` | No | Your access token is invalid, expired, or revoked. | Refresh the token (or re-authenticate), then retry. |
| 403 | `AUTHORIZATION_FAILED` | No | The user account is not active. | Reactivate the account, or contact support. |
| 404 | `RESOURCE_NOT_FOUND` | No | The `redemptionId` doesn't exist. | Check the ID and retry. |
| 400 | `VALIDATION_ERROR` | No | The redemption needs a `businessId` you didn't provide, or another required input is malformed/missing. Check `message` for which field. | Fix the request and retry. |
| 422 | `REQUIRED_FIELDS_MISSING` | No | The user's profile is missing information this redemption requires (e.g. email, date of birth). See `details.missingFields`. | Prompt the user to complete their profile, then retry. |
| 422 | `BOOKING_REQUIREMENT_NOT_MET` | No | The user doesn't have a qualifying booking for this redemption. See `details.requirementType`. | Prompt the user to make/complete a qualifying booking, then retry. |
| 422 | `USER_STATUS_RESTRICTED` | No | The user's account status/tier is too low for this redemption. | Not actionable by retry — inform the user of the required tier. |
| 404 | `USER_NOT_FOUND` / `WALLET_NOT_FOUND` | No | The user or their wallet could not be resolved. | Rare/unexpected — contact support if it persists. |
| 422 | `INSUFFICIENT_BALANCE` | No | The user doesn't have enough token balance to pay for this redemption. | Prompt the user to top up, then retry. Nothing was created/charged. |
| 400 | `LOCATION_VERIFICATION_REQUIRED` | No | This redemption has a country restriction and we couldn't determine the user's location. | Ensure location data is sent with the request, then retry. |
| 422 | `GEOGRAPHIC_RESTRICTION` | No | This redemption isn't available in the user's country. | Not actionable by retry. |
| 409 | `REDEMPTION_ALREADY_PROCESSING` | No | The user already has a redemption for this item in progress. | Don't retry immediately — poll the redemption's status instead (see below) and let it finish. |
| 409 | `MAX_REDEMPTION` | No | This redemption has run out of total supply. | Not actionable by retry — inform the user it's sold out. |
| 409 | `USER_LIMIT` | No | The user has already redeemed this item the maximum number of times allowed. | Not actionable by retry. |
| 422 | `WALLET_MISSING_SIGNING` | No | The user's wallet needs a one-time signing-account setup before it can transact. | Complete wallet/signing setup for the user, then retry. |
| 422 | `BUSINESS_NOT_MINTER` | No | The business isn't authorized to send this reward token. | Contact support/admin to authorize the business as a minter. |
| 500 | `SYSTEM_ERROR` | Yes | An unexpected error occurred while processing the transaction (e.g. temporary network/RPC issue). The redemption attempt was marked as failed — no funds/tokens were left in an inconsistent state. | Safe to retry after a short delay. If it keeps happening, contact support with the timestamp. |


**Note on `409 REDEMPTION_ALREADY_PROCESSING`**: this is expected behavior for double-submits
(e.g. a user double-tapping "Redeem"), not necessarily an error state. Debounce the redeem button
client-side, and if you do receive this code, poll `GET /redemptions/redeems/{redeemId}` (using the
ID from the error) instead of resubmitting.

## `GET /redemptions/redeems/me`, `GET /redemptions/redeems`, `GET /redemptions/redeems/{redeemId}`

| HTTP | `code` | What it means | What to do |
|  --- | --- | --- | --- |
| 401 | `AUTHENTICATION_REQUIRED` | You're not signed in, or (on `/{redeemId}`) the redeem doesn't belong to the signed-in user. | Sign in, or don't request redeems that aren't the current user's. |
| 404 | `RESOURCE_NOT_FOUND` | On `/{redeemId}`: the ID doesn't exist. | Check the ID and retry. |
| 400 | `VALIDATION_ERROR` | On admin list queries: you filtered by both `userId` and `businessId` at once (not supported — use one or the other). | Fix the request and retry. |


## `details` payload reference

Some errors include a `details` object with extra structured data:

| `code` | `details` shape | Example |
|  --- | --- | --- |
| `REQUIRED_FIELDS_MISSING` | `{ missingFields: string[] }` | `{ "missingFields": ["email", "dateOfBirth"] }` |
| `BOOKING_REQUIREMENT_NOT_MET` | `{ requirementType: string }` | `{ "requirementType": "active" }` |
| `REDEMPTION_ALREADY_PROCESSING` | *(none — the redeem ID and status are embedded in `message`)* | — |


## Quick reference — all codes for this flow, by HTTP status

| HTTP | Codes |
|  --- | --- |
| 400 | `VALIDATION_ERROR`, `LOCATION_VERIFICATION_REQUIRED` |
| 401 | `AUTHENTICATION_REQUIRED`, `INVALID_TOKEN`, `TOKEN_EXPIRED`, `TOKEN_REVOKED` |
| 403 | `AUTHORIZATION_FAILED` |
| 404 | `RESOURCE_NOT_FOUND`, `USER_NOT_FOUND`, `WALLET_NOT_FOUND` |
| 409 | `REDEMPTION_ALREADY_PROCESSING`, `MAX_REDEMPTION`, `USER_LIMIT` |
| 422 | `REQUIRED_FIELDS_MISSING`, `INSUFFICIENT_BALANCE`, `WALLET_MISSING_SIGNING`, `BOOKING_REQUIREMENT_NOT_MET`, `USER_STATUS_RESTRICTED`, `GEOGRAPHIC_RESTRICTION`, `BUSINESS_NOT_MINTER` |
| 500 | `SYSTEM_ERROR` |


For the underlying error envelope, categories, and general client-side handling patterns, see the
[Error Handling guide](/8.error-handling).

## Related documentation

- [PERS Error Handling](https://docs.pers.ninja/8.error-handling) — response envelope, categories, correlation IDs
- [PERS Authentication Guide](https://docs.pers.ninja/5.authentication-guide) — the 401/403 auth errors referenced above
- [PERS SDK Introduction](https://docs.pers.ninja/sdk-intro) — `sdk.redemptions.*`, `PersApiError`/`AuthenticationError`
- [PERS SDK Reference](https://docs.pers.ninja/sdk-reference/readme) — full generated API reference