This page lists every error response your application can receive when claiming a campaign reward (POST /campaigns/claims) and when reading claim history (GET /campaigns/claims/me, GET /campaigns/claims).
For the general error envelope, categories, and client-handling patterns, see the Error Handling guide — this page only covers what's specific to claiming.
If you're on the PERS SDK rather than calling the REST API directly, claims go through sdk.campaigns.claimCampaign(...) and surface the same error codes wrapped in the SDK's own error classes (SDK Error Handling):
import { PersApiError, AuthenticationError } from "@explorins/pers-sdk/core";
try {
const claim = await sdk.campaigns.claimCampaign({
campaignId: "campaign-123",
businessId: "business-456" // optional business context
});
} 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("Claim failed:", error.code, error.status, error.category, error.retryable);
}
}| HTTP | code | Retryable | What it means | What to do |
|---|---|---|---|---|
| 401 | AUTHENTICATION_REQUIRED | No | You're not signed in. | 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. |
| 404 | CAMPAIGN_NOT_FOUND | No | The campaignId/triggerSourceId you sent doesn't match any campaign. | Check the ID and retry. |
| 404 | TRIGGER_SOURCE_NOT_FOUND | No | The triggerSourceId you sent doesn't exist (e.g. a stale/invalid QR code). | Re-scan/re-fetch a valid trigger source. |
| 400 | VALIDATION_ERROR | No | Malformed input — e.g. campaignId doesn't match the trigger source's campaign, the campaign has multiple trigger sources and you didn't specify which one, or neither ID was provided. Check message for specifics. | Fix the request and retry. |
| 409 | CAMPAIGN_NOT_ACTIVE | No | This campaign isn't currently active. | Not actionable by retry. |
| 409 | CAMPAIGN_ENDED / CAMPAIGN_NOT_STARTED | No | The campaign's date window doesn't cover now. | Not actionable by retry. |
| 422 | CAMPAIGN_USER_INFO_REQUIRED | No | Claiming requires a user profile field (or a user identifier for business/system claims) that wasn't provided. See details.missingFields. | Prompt for the missing field/identifier and retry. |
| 422 | CAMPAIGN_BUSINESS_REQUIRED | No | This campaign must be claimed on behalf of a business, and none was provided. | Provide business context and retry. |
| 400 | CAMPAIGN_LOCATION_REQUIRED | No | This campaign checks proximity to a location and no user location was sent. | Send latitude/longitude and retry. |
| 422 | CAMPAIGN_DISTANCE_EXCEEDED | No | The user is too far from the required location. | Not actionable by retry — the user needs to be physically closer. |
| 422 | CAMPAIGN_CONDITION_NOT_MET | No | A campaign-specific condition wasn't satisfied (e.g. a custom metadata condition, a per-source/global claim limit, or a trigger-type mismatch). Check message for which condition. | Depends on the condition — usually not actionable by retry. |
| 400 | LOCATION_VERIFICATION_REQUIRED | No | This campaign 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 campaign isn't available in the user's country. | Not actionable by retry. |
| 409 | CAMPAIGN_ALREADY_CLAIMED | No | The user (or the given externalReferenceId/business) has already claimed this campaign. Permanent — not retryable. | Not actionable by retry. |
| 409 | CAMPAIGN_CLAIM_ALREADY_PROCESSING | No | The user has a claim for this campaign currently in progress. Transient. | Don't retry immediately — poll GET /campaigns/claims/me?campaignId=... (using the claim ID from the error) instead of resubmitting. |
| 409 | CAMPAIGN_CLAIM_LIMIT_REACHED | No | The user has already claimed this campaign the maximum number of times allowed. | Not actionable by retry. |
| 409 | CAMPAIGN_DAILY_LIMIT_REACHED | No | The user (or business) has reached today's claim limit for this campaign. | Retry after the daily window resets. |
| 409 | CAMPAIGN_GLOBAL_LIMIT_REACHED | No | This campaign has reached its total claim limit across all users. | Not actionable by retry — the campaign is exhausted. |
| 409 | CAMPAIGN_GLOBAL_DAILY_LIMIT_REACHED | No | This campaign has reached its total claim limit for today across all users. | Retry after the daily window resets. |
| 422 | WALLET_MISSING_SIGNING | No | The user's (or business's) wallet needs a one-time signing-account setup before it can receive the reward. | Complete wallet/signing setup, then retry. |
| 429 | CAMPAIGN_COOLDOWN_ACTIVE | Yes (after cooldown) | The user must wait between claims for this campaign. See details.resetTime (ISO 8601 timestamp) and details.window. | Retry after the cooldown elapses. |
| 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. |
| 422 | CAMPAIGN_NO_TRIGGER | No | The campaign is misconfigured (no trigger). This is a server-side configuration issue, not a client error. | Contact support — this needs admin correction of the campaign setup. |
| 500 | SYSTEM_ERROR | Yes | An unexpected error occurred while processing the reward transaction. The claim was marked as failed — no reward was left in an inconsistent state. | Safe to retry after a short delay. If it keeps happening, contact support with the timestamp. |
Note on 409 CAMPAIGN_CLAIM_ALREADY_PROCESSING: this is expected behavior for double-submits (e.g. a user double-tapping "Claim"), not necessarily an error state — debounce the claim button client-side.
Note on completion-threshold campaigns: if a campaign has a claim-count threshold that hasn't been reached yet, a successful claim (200) is returned without a reward transaction — this is expected, not an error. The reward is granted once the threshold is met on a later claim.
| HTTP | code | What it means | What to do |
|---|---|---|---|
| 401 | AUTHENTICATION_REQUIRED | You're not signed in. | Sign in and retry. |
| 400 | VALIDATION_ERROR | Admin list queries: you filtered by both userId and businessId at once (not supported). | Use one or the other. |
There is no GET /campaigns/claims/{id} endpoint — retrieve claims via the list/me endpoints.
| HTTP | Codes |
|---|---|
| 400 | VALIDATION_ERROR, CAMPAIGN_LOCATION_REQUIRED, LOCATION_VERIFICATION_REQUIRED |
| 401 | AUTHENTICATION_REQUIRED, INVALID_TOKEN, TOKEN_EXPIRED, TOKEN_REVOKED |
| 404 | CAMPAIGN_NOT_FOUND, TRIGGER_SOURCE_NOT_FOUND |
| 409 | CAMPAIGN_ALREADY_CLAIMED, CAMPAIGN_CLAIM_ALREADY_PROCESSING, CAMPAIGN_CLAIM_LIMIT_REACHED, CAMPAIGN_DAILY_LIMIT_REACHED, CAMPAIGN_GLOBAL_LIMIT_REACHED, CAMPAIGN_GLOBAL_DAILY_LIMIT_REACHED, CAMPAIGN_NOT_ACTIVE, CAMPAIGN_ENDED, CAMPAIGN_NOT_STARTED |
| 422 | WALLET_MISSING_SIGNING, CAMPAIGN_USER_INFO_REQUIRED, CAMPAIGN_CONDITION_NOT_MET, CAMPAIGN_BUSINESS_REQUIRED, CAMPAIGN_DISTANCE_EXCEEDED, GEOGRAPHIC_RESTRICTION, BUSINESS_NOT_MINTER, CAMPAIGN_NO_TRIGGER |
| 429 | CAMPAIGN_COOLDOWN_ACTIVE |
| 500 | SYSTEM_ERROR |
For the underlying error envelope, categories, and general client-side handling patterns, see the Error Handling guide.
- PERS Error Handling — response envelope, categories, correlation IDs
- PERS Authentication Guide — the 401 auth errors referenced above
- PERS SDK Introduction —
sdk.campaigns.claimCampaign,PersApiError/AuthenticationError - PERS SDK Reference — full generated API reference