Public changelog for the PERS Platform API. Tracking started April 2026.
Current Version: 2.0.59
- Improved data retention handling: old audit, AI and redirect records are now removed automatically after 400 days, and GDPR-deleted user data is anonymized more completely
- Breaking: Several campaign, business, and webhook error responses now return 422 (previously 403 or 400) to correctly reflect them as business-rule errors rather than authorization or validation failures
- Breaking: Campaign not-active, not-started, ended, and webhook-inactive errors now return 409 (previously 400) to correctly reflect a resource-state conflict
- Breaking: Token-type-not-found and redemption-not-found errors now return 404 (previously 400)
- Breaking: Smart wallet processing failures now return 500 (previously 422); webhook forward failures now return 503 (previously 502)
- Breaking: Campaign cooldown errors are now categorized as RATE_LIMIT instead of DOMAIN_RULE
- Breaking: In-progress campaign claims now return a distinct CAMPAIGN_CLAIM_ALREADY_PROCESSING error code (409) instead of CAMPAIGN_ALREADY_CLAIMED
- Deprecated CampaignBusinessEngagement - use TriggerSource.businessId for business participation instead
- Updated ?include=businesses to extract business IDs from TriggerSources instead of deprecated CampaignBusinessEngagement
- Fixed GET /healthcheck returning a server error
- Fixed several endpoints (businesses, campaigns, tokens, transactions, redemptions, users, and more) returning a generic server error instead of a proper validation error when given a malformed ID
- Fixed GET /analytics/tags returning a server error when no entityTypes filter was specified
- Added a Trigger Type field and filter to the campaign claims list, and fixed the campaign claims table's user/business/system filter not working
- Fixed GET /trigger-sources being publicly readable without authentication; businesses now only see their own trigger sources
- Fixed GET /trigger-sources campaignId filter silently being ignored
- Fixed validation error responses missing the target field identifying which input failed
- Fixed campaign cooldown errors missing reset-time details
- Fixed redemption and campaign claim requests sometimes returning a generic server error instead of the actual business error
- Security: Internal server errors (500s) no longer echo raw database/driver error text back to clients
- Added ownerBusinessId field to campaigns, redemptions, and token metadata for business ownership tracking
- Added ?include=ownerBusiness query parameter to load owner business entity on campaigns, redemptions, and token metadata
- Added ?ownerBusinessId filter parameter to campaigns, redemptions, and token metadata endpoints
- New attribution tracking for analytics across all entities (users, claims, redemptions, bookings)
- Auto-detects channel/source from User-Agent and Referer headers
- SDK can send x-source-* headers for campaign/medium tracking
- Standalone serverless function for booking operations
- Users now have registrationSource field for analytics
- Renamed "reservation" terminology to "booking" for redemption eligibility requirements
- Simplified booking requirement types: 'active', 'future', 'past', 'active_future', 'any' (no 'none')
- Redemptions now return userInfo and booking data via ?include=userInfo,booking
- GET /bookings endpoint now supports userId and businessId query filters
- Analytics endpoints now support ownerBusinessId filter for business-specific reporting
- User analytics endpoint now returns totalClaims, totalRedemptions, totalBurns metrics
- Analytics results now include campaign/redemption names and owner business ID automatically
- Added metadataUri field to transactions for tracking IPFS/storage URLs of NFT metadata
- Added structured error codes for token refresh failures (REFRESH_TOKEN_EXPIRED, REFRESH_TOKEN_REVOKED)
- Added PUT and DELETE endpoints for redemption types
- Improved DPoP clock tolerance for corporate environments (10 min past, 5 min future)
- Added dateFrom and dateTo query filters to campaign claims and redemption redeems endpoints
- Added multi-tag filtering (OR match) with tags parameter on campaigns, redemptions, and businesses
- Added search parameter to GET /businesses endpoint
- Added isActive option for immediate activation when creating redemptions and token metadata
- Fixed PUT /users/me, POST /users/info, and PUT /users/:identifier to return virtual wallets
- Fixed date parameter validation - invalid date formats now return 400 Bad Request
- Added startDate, endDate, and terms fields to Redemption entity
- Added registrationNumber field to Business entity for company registration/VAT numbers
- Added startDate and endDate query filters to GET /campaigns and GET /redemptions
- Added claimCount include option for campaigns (?include=claimCount)
- Added redeemCount include option for redemptions (?include=redeemCount)
- Improved error responses to preserve original error domain instead of inferring from URL path
- Added template interpolation for ERC721 token name/description using {{placeholder}} syntax
- Added {{user.firstName}}, {{campaign.name}}, {{business.displayName}}, {{context.xxx}}, {{defaults.xxx}} placeholder support
- Added context field to POST /transactions for direct ERC721 mints with template interpolation
- Template interpolation is free (no AI cost) and instant
- Improved API documentation for dynamic context and template interpolation in Swagger
- Fixed AI prompts now support {{context.xxx}} placeholder interpolation
- Fixed allowExternalContextOverwrite not persisting on token metadata update
- Added dynamic context support for ERC721 token generation with AI prompts
- Added trigger-based validity for NFT expiry (validityDate, validityEndDate, validityDuration)
- Added
contextfield to TriggerSource for admin-defined dynamic data - Added
contextfield to CampaignClaimRequest for user-provided context - Added
contextfield to Redemption for admin-defined dynamic data - Added
contextfield to RedemptionRedeemRequest for user-provided context - Added
defaultPromptContextto TokenMetadata for AI prompt defaults
- Added
allowExternalContextOverwriteto TokenMetadata for security control
- Added
logoUrlfield to Campaign, Business, Redemption DTOs for branding customization - Added
projectLogoUrlfield to Tenant DTOs for project branding - Added
termsfield to CampaignTrigger for Terms & Conditions support
- Enhanced API documentation for campaign claims with detailed integration guides
- GET /tenants/me now returns full tenant data for authenticated tenant admins
- GET /businesses/account/:address now uses wallet-based lookup for multi-chain support
- Improved security for Stripe credential storage with encryption at rest
- Added OIDC discovery endpoint at
/.well-known/openid-configurationfor enterprise SSO integration
- Improved platform stability for high-traffic multi-tenant scenarios
- Webhook trigger endpoint now returns structured response with execution metadata (executionId, statusCode, durationMs)