# Create redemption (Admin)

Create a new redemption with administrative privileges. Replaces POST /redemption/admin

Endpoint: POST /redemptions
Version: 2.0.53
Security: authJWT

## Request fields (application/json):

  - `ownerBusinessId` (string)
    Optional owner business ID. If set, this redemption belongs to a specific business. If null/omitted, the redemption is tenant-owned (system-level).

  - `isActive` (boolean)
    Whether the redemption is active immediately on creation. Defaults to false.

  - `creditTokenAmount` (number)
    The amount of credit tokens needed to redeem the redemption tokens

  - `description` (string)
    Description of the redemption

  - `name` (string)
    Name of the redemption

  - `imageUrl` (string)
    Image url of the redemption

  - `logoUrl` (string)
    Logo URL for the redemption (smaller icon/avatar)

  - `terms` (string)
    Terms and conditions for the redemption. Plain text or HTML. Multi-language handling at app level.

  - `startDate` (string)
    Redemption start date

  - `endDate` (string)
    Redemption end date

  - `redemptionTypeId` (number)
    Redemption type id

  - `minUserStatusTypeId` (number)
    min user status type id to redeem the redemption

  - `maxTotalSupply` (number)
    The maximum total supply of the redemption

  - `order` (number)
    The order of the redemption

  - `maxPerUser` (number)
    The maximum redemption per user

  - `countryCodeRestrictions` (array)
    Country code restrictions as an array of strings (e.g., ["NOT_ES", "FR"])
    Example: ["NOT_ES","FR"]

  - `tags` (array)
    Tags for categorization and filtering
    Example: ["rewards","partner","premium"]

  - `context` (object)
    **ERC721 only** - Admin-defined dynamic context for AI prompts and trigger-based validity. Always applied to tokens minted from this redemption. Special keys: `validityDate`, `validityEndDate`, `validityDuration` for token expiry.
    Example: {"eventName":"Summer Sale","validityDuration":30}

  - `specificRequiredUserFields` (array)
    Required user fields specific to this redemption (beyond type defaults). Can include built-in field keys (email, firstName) or custom field UUIDs.
    Example: ["email","firstName","countryOfResidence"]

  - `bookingRequirement` (string)
    Booking requirement for this redemption. If set, defines what type of booking user needs. Omit or null = no booking required.
    Enum: "active", "future", "past", "active_future", "any"

  - `priceTokenUnits` (array)
    Optional token units the user must transfer/burn as payment (ERC20, ERC1155, or ERC721). Use resolveByBusiness=true for stamp-token redemptions where the tokenId is resolved per business at redeem time.
    Example: [{"tokenEntityId":"uuid","tokenMetadataIncrementalId":1,"amount":9}]

  - `priceTokenUnits.tokenEntityId` (string, required)
    Token contract entity UUID

  - `priceTokenUnits.tokenMetadataIncrementalId` (number)
    Explicit on-chain tokenId (ERC1155) or template lookup key (ERC721). Leave null when resolveByBusiness=true.

  - `priceTokenUnits.resolveByBusiness` (boolean)
    When true, the tokenMetadataIncrementalId is resolved at redeem time by looking up the TokenMetadata whose ownerBusinessId matches the redemption's ownerBusinessId. Requires the token to be a stampToken. Cannot be combined with an explicit tokenMetadataIncrementalId.

  - `priceTokenUnits.resolveTag` (string)
    Discriminator tag used when resolveByBusiness=true and a business has multiple stamp tokenIds on the same contract (e.g. "coffee", "meal"). Must match at least one tag on the resolved TokenMetadata. When omitted, a single unambiguous match is expected.

  - `priceTokenUnits.amount` (number, required)
    Amount of tokens the user must transfer as payment.

## Response 200 fields (application/json):

  - `id` (string, required)
    Redemption id

  - `ownerBusinessId` (string, required)
    Optional owner business ID. If set, this redemption belongs to a specific business. If null, the redemption is tenant-owned (system-level).

  - `description` (string, required)
    Description of the redemption

  - `name` (string, required)
    Name of the redemption

  - `imageUrl` (string, required)
    Image url of the redemption

  - `logoUrl` (string, required)
    Logo URL for the redemption

  - `terms` (string, required)
    Terms and conditions for the redemption. Plain text or HTML. Multi-language handling at app level.

  - `startDate` (string, required)
    Redemption start date

  - `endDate` (string, required)
    Redemption end date

  - `isActive` (boolean, required)
    Redemption is active

  - `approval` (object, required)
    Approval metadata for this redemption.

  - `approval.status` (string, required)
    Approval state: pending_approval = awaiting admin review, approved = active, rejected = denied.
    Enum: "pending_approval", "approved", "rejected"

  - `approval.approvedAt` (string)
    Timestamp when the entity was approved or rejected by an admin.

  - `approval.approvedBy` (string)
    Admin user ID who approved or rejected this entity.

  - `approval.rejectionReason` (string)
    Reason provided when the entity was rejected.

  - `creditTokenAmount` (number, required)
    The amount of credit tokens needed to redeem the redemption tokens

  - `type` (object, required)
    The redemption type

  - `type.name` (string, required)
    Redemption type name

  - `type.description` (string)
    Redemption type description

  - `type.imageUrl` (string)
    Redemption type image url

  - `type.baseRequiredUserFields` (array)
    Base required user fields for all redemptions of this type. Can include built-in field keys (email, firstName) or custom field definition UUIDs.

  - `type.bookingRequirement` (object)
    Default booking requirement config for all redemptions of this type. Individual redemptions can override.

  - `type.bookingRequirement.type` (string, required)
    Type of booking required for eligibility.
    Enum: "active", "future", "past", "active_future", "any"

  - `type.bookingRequirement.eligibleBusinessIds` (array)
    Specific business IDs where booking must be. `null` = any business allowed, `[]` = no businesses allowed (blocks all). Combined with eligibleBusinessTypeIds via OR.

  - `type.bookingRequirement.eligibleBusinessTypeIds` (array)
    Business type IDs where booking must be. `null` = any type allowed, `[]` = no types allowed (blocks all). Combined with eligibleBusinessIds via OR.

  - `type.id` (number, required)
    Redemption type id

  - `priceTokenUnits` (array, required)
    Optional token units the user must transfer/burn as payment (ERC20, ERC1155, or ERC721). When set, these are transferred from user → business in addition to (or instead of) creditTokenAmount.

  - `priceTokenUnits.tokenEntityId` (string, required)
    Token contract entity UUID

  - `priceTokenUnits.tokenMetadataIncrementalId` (number)
    Explicit on-chain tokenId (ERC1155) or template lookup key (ERC721). Leave null when resolveByBusiness=true.

  - `priceTokenUnits.resolveByBusiness` (boolean)
    When true, the tokenMetadataIncrementalId is resolved at redeem time by looking up the TokenMetadata whose ownerBusinessId matches the redemption's ownerBusinessId. Requires the token to be a stampToken. Cannot be combined with an explicit tokenMetadataIncrementalId.

  - `priceTokenUnits.resolveTag` (string)
    Discriminator tag used when resolveByBusiness=true and a business has multiple stamp tokenIds on the same contract (e.g. "coffee", "meal"). Must match at least one tag on the resolved TokenMetadata. When omitted, a single unambiguous match is expected.

  - `priceTokenUnits.amount` (number, required)
    Amount of tokens the user must transfer as payment.

  - `mechanism` (string, required)
    Computed payment mechanism. Use this for rendering decisions (FREE = no payment, CREDIT = platform credit tokens, TOKEN_PAY = ERC20 transfer, TOKEN_BURN = ERC1155/ERC721 stamp/NFT, MIXED = credit + token). Never stored — derived from creditTokenAmount and priceTokenUnits.
    Enum: "FREE", "CREDIT", "TOKEN_PAY", "TOKEN_BURN", "MIXED"

  - `tokenUnits` (array, required)
    The redemption tokens to be redeemed

  - `tokenUnits.id` (string, required)
    Database UUID for this token unit

  - `tokenUnits.token` (object, required)
    The token contract this unit references

  - `tokenUnits.token.id` (string, required)
    Database UUID for this token contract entity

  - `tokenUnits.token.contractAddress` (string, required)
    Smart contract address deployed on the blockchain

  - `tokenUnits.token.metadata` (array)
    Token metadata templates - blueprints used when minting NFTs. Each template defines properties (name, image, expiry) and has a unique tokenMetadataIncrementalId. Null for ERC20 (Points) tokens.

  - `tokenUnits.token.metadata.ownerBusinessId` (string)
    Optional owner business ID. If set, this token metadata belongs to a specific business. If null, the token metadata is tenant-owned (system-level).

  - `tokenUnits.token.metadata.isActive` (boolean)
    Whether this template is active and can be used for minting

  - `tokenUnits.token.metadata.imageUrl` (string)
    This is the URL to the image of the item. Can be just about any type of image (including SVGs, which will be cached into PNGs by OpenSea), IPFS or Arweave URLs or paths. We recommend using a minimum 3000 x 3000 image.

  - `tokenUnits.token.metadata.externalUrl` (string)
    This is the URL that will appear below the asset's image

  - `tokenUnits.token.metadata.description` (string)
    A human-readable description of the item. Markdown is supported.
**ERC721 Template Interpolation:** Supports `{{placeholder}}` syntax for dynamic personalization at mint time (without AI costs).
**Available placeholders:**
- `{{user.firstName}}`, `{{user.lastName}}` - User data
- `{{campaign.name}}`, `{{business.displayName}}` - Campaign/business
- `{{context.xxx}}` - Dynamic context values (TriggerSource.context or claim request)
- `{{defaults.xxx}}` - Default values from defaultPromptContext

**Example:** `"Thank you {{user.firstName}} for visiting {{business.displayName}}! Your exclusive reward for {{context.eventName}}."`
**Processing order:** Template interpolation runs first, then AI processing (if configured) can further enhance the result.

  - `tokenUnits.token.metadata.name` (string)
    Name of the item.
**ERC721 Template Interpolation:** Supports `{{placeholder}}` syntax for dynamic personalization at mint time (without AI costs).
**Available placeholders:**
- `{{user.firstName}}`, `{{user.lastName}}` - User data
- `{{campaign.name}}`, `{{business.displayName}}` - Campaign/business
- `{{context.xxx}}` - Dynamic context values (TriggerSource.context or claim request)
- `{{defaults.xxx}}` - Default values from defaultPromptContext

**Example:** `"Welcome {{context.guestName}} - VIP Pass"` or `"{{user.firstName}}'s {{campaign.name}} Reward"`
**Processing order:** Template interpolation runs first, then AI processing (if configured) can further enhance the result.

  - `tokenUnits.token.metadata.validityType` (string)
    **ERC721 only** - Validity type defines how token expiry is calculated. Use with validityDuration for relative types. Ignored for ERC1155 tokens.
    Enum: "fixed_date", "days_from_issuance", "hours_from_issuance", "months_from_issuance", "end_of_month", "end_of_year", "trigger_date", "trigger_date_range", "days_from_trigger", "hours_from_trigger"

  - `tokenUnits.token.metadata.validityDuration` (number)
    **ERC721 only** - Duration in days or hours (used with days_from_issuance, hours_from_issuance validity types). Ignored for ERC1155 tokens.
    Example: 30

  - `tokenUnits.token.metadata.expiryDate` (string)
    Fixed expiry date (used with fixed_date validity type). For other validity types, this is computed at issuance time.

  - `tokenUnits.token.metadata.animationUrl` (string)
    A URL to a multi-media attachment for the item. The file extensions GLTF, GLB, WEBM, MP4, M4V, OGV, and OGG are supported, along with the audio-only extensions MP3, WAV, and OGA. Animation_url also supports HTML pages, allowing you to build rich experiences and interactive NFTs using JavaScript canvas, WebGL, and more. Scripts and relative paths within the HTML page are now supported. However, access to browser extensions is not supported.

  - `tokenUnits.token.metadata.youtubeUrl` (string)
    A URL to a YouTube video (only used if animation_url is not provide

  - `tokenUnits.token.metadata.creatorAccountAddress` (string)
    Creator Address

  - `tokenUnits.token.metadata.previewUrl` (string)
    Preview Url

  - `tokenUnits.token.metadata.tags` (array)
    Tags for categorization and filtering
    Example: ["summer","vip","limited-edition"]

  - `tokenUnits.token.metadata.aiPromptConfigs` (array)
    **ERC721 only** - AI prompt configurations for dynamic content generation at mint time.
AI processing runs AFTER template interpolation, so prompts can reference already-interpolated values.
Results are mapped by key to override static fields (name, description, imageUrl). Ignored for ERC1155 tokens.
**Note:** For simple personalization without AI costs, use `{{placeholder}}` syntax directly in name/description fields instead.
    Example: [{"type":"TEXT_GENERATION","key":"name","prompt":"Generate a unique reward name for {{user.firstName}} at {{business.displayName}}"},{"type":"IMAGE_GENERATION","key":"imageUrl","prompt":"A {{defaults.…

  - `tokenUnits.token.metadata.aiPromptConfigs.type` (string, required)
    Type of AI operation to perform
    Enum: "TEXT_GENERATION", "IMAGE_GENERATION"

  - `tokenUnits.token.metadata.aiPromptConfigs.key` (string, required)
    Key to store the generated result under
    Example: name

  - `tokenUnits.token.metadata.aiPromptConfigs.prompt` (string, required)
    The prompt to send to the AI model. Supports dynamic placeholders that are replaced with context values.
**Available Placeholders:**
- `{{user.firstName}}`, `{{user.lastName}}`, `{{user.email}}` - User profile data
- `{{campaign.name}}`, `{{campaign.description}}` - Campaign data
- `{{tenant.projectName}}`, `{{tenant.projectDescription}}` - Tenant/project data
- `{{business.displayName}}`, `{{business.name}}` - Business data
- `{{triggerSource.name}}`, `{{triggerSource.type}}` - Trigger source data
- `{{redemption.name}}`, `{{redemption.description}}` - Redemption data
- `{{context.xxx}}` - Dynamic context from TriggerSource.context or CampaignClaimRequest.context
- `{{defaults.xxx}}` - Default values from TokenMetadata.defaultPromptContext

**Context Sources:**
1. TriggerSource.context (admin QR/NFC) - Always applied
2. CampaignClaimRequest.context (user API) - Only if allowExternalContextOverwrite=true
3. TokenMetadata.defaultPromptContext - Always applied as fallback

**Examples:**
- `"Generate a welcome message for {{user.firstName}} at {{business.displayName}}"`
- `"Create a VIP badge for guest {{context.guestName}} in room {{context.roomNumber}}"`
- `"Design a {{defaults.style}} reward image for {{campaign.name}}"`
    Example: Generate a unique reward name for {{user.firstName}} visiting {{business.displayName}}

  - `tokenUnits.token.metadata.allowExternalContextOverwrite` (boolean)
    **ERC721 only** - Allow USER-PROVIDED external context from claim/redeem requests to be used in template interpolation and AI prompts.
**Security model:**
- `false` (default): Only admin-controlled context (TriggerSource.context, Redemption.context, defaultPromptContext) is available
- `true`: User-provided context from API requests is also merged and available as `{{context.xxx}}`

**Note:** Admin-controlled context (QR/NFC data) is ALWAYS applied regardless of this flag.
    Example: true

  - `tokenUnits.token.metadata.defaultPromptContext` (object)
    **ERC721 only** - Default context values for template interpolation and AI prompts. Available as `{{defaults.xxx}}` placeholders.
Always applied regardless of allowExternalContextOverwrite. Use for brand defaults, styling preferences, or fallback values.
**Example usage in name:** `"{{defaults.brand}} - {{user.firstName}}'s Reward"`
    Example: {"brand":"PERS Rewards","style":"minimal","defaultLocation":"Online"}

  - `tokenUnits.token.metadata.businessIds` (array)
    Business IDs where this token can be redeemed/spent. Empty array means any business in the tenant.
    Example: ["business-uuid-1","business-uuid-2"]

  - `tokenUnits.token.metadata.webhookId` (string)
    **ERC721 only** - Webhook ID for dynamic data fetch at mint time.
If set, the webhook is executed before metadata generation and response data is merged into the NFT.
**Use cases:**
- Fetch guest data from PMS/CRM systems
- Get real-time pricing or availability
- Retrieve user-specific content from external APIs

**Processing order:** Webhook fetch runs AFTER template interpolation but BEFORE AI processing.
    Example: webhook-uuid-for-pms-lookup

  - `tokenUnits.token.metadata.webhookPayloadTemplate` (object)
    **ERC721 only** - Payload template for webhook request.
Supports `{{placeholder}}` interpolation with same context as AI prompts.
**Example:**

```json
{
  "userId": "{{user.id}}",
  "campaignId": "{{campaign.id}}",
  "bookingId": "{{context.bookingId}}"
}
```
    Example: {"userId":"{{user.id}}","bookingId":"{{context.bookingId}}"}

  - `tokenUnits.token.metadata.webhookFieldMapping` (object)
    **ERC721 only** - Field mapping from webhook response to metadata fields.
Keys are webhook response paths (dot notation for nested), values are target field names.
**Mapping rules:**
- Explicit mapping: `"response.path": "targetField"`
- Known fields (name, description, imageUrl): auto-mapped if present
- Unknown fields or `"attributes"` target: become NFT attributes

**Example:**

```json
{
  "guestName": "name",
  "data.guest.image": "imageUrl",
  "roomNumber": "attributes",
  "checkInDate": "attributes"
}
```
If null/undefined, uses auto-mapping.
    Example: {"guestName":"name","roomNumber":"attributes","data.image":"imageUrl"}

  - `tokenUnits.token.metadata.consumable` (boolean)
    Whether this token template is consumable (burned on redemption) or collectible (transferred). Applies to ERC1155 and ERC721 only — ERC20 is always transferred. When true, the token is destroyed (burn) when used as payment in a redemption. Defaults to true — most token templates are consumable. Set to false for collectible/non-burning use cases. Mirrored as a consumable attribute in the on-chain IPFS metadata.

  - `tokenUnits.token.metadata.id` (string, required)
    Database UUID for this token metadata template

  - `tokenUnits.token.metadata.animationWeb3StorageUrl` (string)
    IPFS/Arweave URL for animation file - immutable web3 storage

  - `tokenUnits.token.metadata.imageWeb3StorageUrl` (string)
    IPFS/Arweave URL for image - immutable web3 storage

  - `tokenUnits.token.metadata.web3StorageUrl` (string)
    IPFS/Arweave URL for complete metadata JSON - this URL is stored on-chain and links to off-chain metadata

  - `tokenUnits.token.metadata.tokenMetadataIncrementalId` (number, required)
    Incremental ID within the token contract. For ERC1155: becomes the on-chain tokenId. For ERC721: lookup key for template to generate unique metadata.

  - `tokenUnits.token.metadata.approval` (object, required)
    Approval metadata for this token metadata.

  - `tokenUnits.token.metadata.mintCount` (number)
    Total number of mints for this token metadata (via ?include=mintCount). Counts SUCCEEDED MINT transactions using tokenMetadataIncrementalId. Accurate for both ERC1155 and ERC721 (post v2.3.49). Historical ERC721 transactions before v2.3.49 are not counted.

  - `tokenUnits.token.metadata.burnCount` (number)
    Total number of burns for this token metadata (via ?include=burnCount). Counts SUCCEEDED BURN transactions using tokenMetadataIncrementalId. Accurate for both ERC1155 and ERC721 (post v2.3.49). Historical ERC721 transactions before v2.3.49 are not counted.

  - `tokenUnits.token.metadata.included` (object)
    Included related entities. Only populated when include parameter is specified.

  - `tokenUnits.token.metadata.included.ownerBusiness` (object)

  - `tokenUnits.token.metadata.included.ownerBusiness.id` (string, required)
    The id of the business, this is unique and will be used to identify the business.

  - `tokenUnits.token.metadata.included.ownerBusiness.email` (string, required)
    The email of the business, this is unique and will be used to identify the business.

  - `tokenUnits.token.metadata.included.ownerBusiness.accountAddress` (string, required)
    The address of the business, this is the address that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.currentSigningAccountId` (string, required)
    Current active signing account ID for external wallet operations

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets` (array, required)
    Business-owned counterfactual smart contract wallets that can receive tokens

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.id` (string, required)
    Unique identifier for the internal wallet

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.ownerType` (string, required)
    Owner type for polymorphic ownership
    Enum: "user", "business", "tenant", "system", "external"

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.ownerId` (string, required)
    Owner ID for polymorphic ownership
    Example: user_123

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.walletManagementType` (string, required)
    Type of internal wallet
    Enum: "custodial", "non-custodial"

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.address` (string, required)
    CREATE2 generated address that can receive tokens

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.chainId` (number, required)
    Blockchain network chain identifier

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.status` (string, required)
    Current status of the wallet
    Enum: "pending", "active", "suspended", "archived"

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.ownerSigningAccountId` (string)
    ID of signing account that owns this internal wallet

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.tags` (array, required)
    Tags associated with the wallet for categorization

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.createdAt` (string, required)
    Timestamp when the wallet was created

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.updatedAt` (string, required)
    Timestamp when the wallet was last updated

  - `tokenUnits.token.metadata.included.ownerBusiness.businessLegalName` (string, required)
    The legal name of the business, this is the name that will be used for legal purposes.

  - `tokenUnits.token.metadata.included.ownerBusiness.registrationNumber` (string, required)
    The business registration number (e.g., company registration, VAT number, EIN)

  - `tokenUnits.token.metadata.included.ownerBusiness.displayName` (string, required)
    The display name of the business, this is the name that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.description` (string, required)
    The description of the business, this is the description that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.shortDescription` (string, required)
    The short description of the business, this is the description that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.websiteUrl` (string, required)
    The website of the business, this is the website that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.imageUrl` (string, required)
    The image of the business, this is the image that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.logoUrl` (string, required)
    Logo URL for the business

  - `tokenUnits.token.metadata.included.ownerBusiness.streetAddress` (string, required)
    The address of the business, this is the address that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.phoneNumber` (string, required)
    The phone number of the business.

  - `tokenUnits.token.metadata.included.ownerBusiness.neighborhood` (string, required)
    Neighborhood/area name (e.g., "West Bay", "Pearl Qatar", "Lusail") - auto-populated from geocoding

  - `tokenUnits.token.metadata.included.ownerBusiness.district` (string, required)
    District/administrative area - auto-populated from geocoding

  - `tokenUnits.token.metadata.included.ownerBusiness.city` (string, required)
    The city of the business, this is the city that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.postalCode` (string, required)
    The postal code of the business, this is the postal code that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.country` (string, required)
    The country of the business (auto-populated from geocoding if coordinates provided)

  - `tokenUnits.token.metadata.included.ownerBusiness.countryCode` (string, required)
    ISO 3166-1 alpha-2 country code (e.g., QA, US, AE) - auto-populated from geocoding
    Example: QA

  - `tokenUnits.token.metadata.included.ownerBusiness.coordsLatitude` (number, required)
    The latitude of the business, this is the latitude that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.coordsLongitude` (number, required)
    The longitude of the business, this is the longitude that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.businessType` (object, required)
    The business type of the business, this is the business type that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.businessType.id` (string)
    Business type id

  - `tokenUnits.token.metadata.included.ownerBusiness.businessType.name` (string, required)
    Business type name

  - `tokenUnits.token.metadata.included.ownerBusiness.businessType.description` (string, required)
    Business type description

  - `tokenUnits.token.metadata.included.ownerBusiness.businessType.code` (string, required)
    Business type code

  - `tokenUnits.token.metadata.included.ownerBusiness.businessType.iconUrl` (string, required)
    Business type icon url

  - `tokenUnits.token.metadata.included.ownerBusiness.isActive` (boolean, required)
    The status of the business, this is the status that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.approval` (object, required)
    Approval metadata for this business.

  - `tokenUnits.token.metadata.included.ownerBusiness.canMintToken` (boolean, required)
    The ability to mint token for the business, this is the ability that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.canChargeToken` (boolean, required)
    The ability to charge token for the business, this is the ability that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.canManageUsers` (boolean, required)
    The ability to manage users for the business, this is the ability that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.canReceiveDonation` (boolean, required)
    The ability to receive donation for the business, this is the ability that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.tags` (array, required)
    Tags for categorization and filtering

  - `tokenUnits.token.metadata.included.ownerBusiness.customData` (object, required)
    Custom business data including Google Places information (placeID, currentOpeningHours)
    Example: {"placeID":"ChIJN1t_tDeuEmsRUsoyG83frY4","currentOpeningHours":{"open_now":true,"weekday_text":["Monday: 9:00 AM – 5:00 PM","Tuesday: 9:00 AM – 5:00 PM"]}}

  - `tokenUnits.token.metadata.included.token` (object)
    Parent token contract info (via ?include=token)

  - `tokenUnits.token.metadata.included.token.chainId` (number)
    Blockchain chain ID (mainnet or testnet)

  - `tokenUnits.token.metadata.included.token.contractAddress` (string)
    Smart contract address

  - `tokenUnits.token.metadata.included.token.type` (string)
    Token type (ERC721, ERC1155)

  - `tokenUnits.token.abi` (object, required)
    this is the abi of the contract, this is the interface of the contract to interact with it

  - `tokenUnits.token.chainId` (number, required)
    this is the chain id of the chain where the token is deployed

  - `tokenUnits.token.abiUrl` (string, required)
    this is the url of the abi of the contract, to be used to fetch the abi of the contract

  - `tokenUnits.token.name` (string)
    this is the name of the token contract

  - `tokenUnits.token.symbol` (string)
    this is the symbol of the token contract, this is optional and can be null

  - `tokenUnits.token.decimals` (number)
    this is the decimals of the token. This is optional and only used for ERC20 tokens (Points)

  - `tokenUnits.token.isActive` (boolean, required)
    This can be used to enable or disable the token

  - `tokenUnits.token.isGallery` (boolean, required)
    This can be used to enable or disable the token for gallery

  - `tokenUnits.token.type` (string, required)
    This is the underlying web3 native type of the token contract
    Enum: "ERC20", "ERC1155", "ERC721"

  - `tokenUnits.token.stampToken` (boolean, required)
    When true, this ERC1155 contract is used as a shared stamp contract. TokenMetadata entries are auto-provisioned per business and can be referenced in redemption priceTokenUnits with resolveByBusiness=true.

  - `tokenUnits.tokenMetadataIncrementalId` (number)
    Token metadata template ID. For ERC1155: becomes on-chain tokenId. For ERC721: lookup key for template to generate unique metadata.

  - `tokenUnits.amount` (number, required)
    Amount of tokens to mint/transfer. For ERC721 this is typically 1, for ERC1155/ERC20 can be any quantity.

  - `tokenUnits.maxAmount` (number)
    Token unit max amount. Applies to MINT/EARN contexts only (e.g. campaign reward capping where user earns tokens per revenue spent). Ignored for spend/transfer contexts such as redemption priceTokenUnits.

  - `order` (number, required)
    The order of the redemption

  - `supply` (object)
    The available supply of the redemption

  - `supply.available` (number, required)
    Number of items available for supply. Null if unlimited.

  - `supply.total` (number, required)
    Total number of items for supply. Null if unlimited.

  - `maxPerUser` (number, required)
    The maximum redemption per user

  - `minUserStatusType` (object, required)
    The minimum user status type to redeem the redemption

  - `minUserStatusType.name` (string, required)
    User Status Type name

  - `minUserStatusType.description` (string)
    User Status Type description

  - `minUserStatusType.minTokenBalance` (string, required)
    User Status Type eligible Token Addresses

  - `minUserStatusType.discountPercentage` (number, required)
    User Status Type discount Rate in percentage

  - `minUserStatusType.imageUrl` (string)
    User Status Type image Url

  - `minUserStatusType.eligibleTokenAddresses` (array)
    Eligible token contract addresses for this status type
    Example: ["0x1234...","0x5678..."]

  - `minUserStatusType.tags` (array)
    Tags for categorization and filtering
    Example: ["vip","premium","gold"]

  - `minUserStatusType.order` (number)
    Explicit ordering for status hierarchy (higher = more prestigious). If not set, falls back to minTokenBalance for ordering.
    Example: 100

  - `minUserStatusType.id` (number, required)
    User Status Type id

  - `countryCodeRestrictions` (object)
    Country code restrictions as an array of strings (e.g., ["NOT_ES", "FR"])
    Example: ["NOT_ES","FR"]

  - `tags` (array, required)
    Redemption tags

  - `context` (object)
    **ERC721 only** - Admin-defined dynamic context for AI prompts and trigger-based validity. Always applied to tokens minted from this redemption. Special keys: `validityDate`, `validityEndDate`, `validityDuration` for token expiry.
    Example: {"eventName":"Summer Sale","validityDuration":30}

  - `specificRequiredUserFields` (array, required)
    Required user fields specific to this redemption (beyond type defaults). Can include built-in field keys (email, firstName) or custom field UUIDs.

  - `requiredUserFields` (array, required)
    All required fields for this redemption (type baseline + extras, deduplicated). Use this for validation.

  - `specificBookingRequirement` (object)
    Redemption-specific booking requirement override. Set to override type default. Omit or null = use type default.

  - `bookingRequirement` (object)
    Effective booking requirement (specific override ?? type default). Use this for validation. Null = no booking required.

  - `included` (object)
    Optional data requested via include parameter. All include-based data lives here for consistency.

  - `included.redeemCount` (number)
    Total number of redeems for this redemption (via ?include=redeemCount)

  - `included.requiredUserInfo` (object)
    Required user info validation status. Only set when requiredUserFields is non-empty + user authenticated. (via ?include=requiredUserInfo)

  - `included.requiredUserInfo.fieldDefinitions` (array)
    Field definitions with isMissing status

  - `included.requiredUserInfo.fieldDefinitions.id` (string, required)
    Unique identifier (UUID)

  - `included.requiredUserInfo.fieldDefinitions.tenantId` (string, required)
    Tenant ID

  - `included.requiredUserInfo.fieldDefinitions.entityType` (string, required)
    Entity type this field applies to
    Enum: "user", "business", "campaign", "redemption"

  - `included.requiredUserInfo.fieldDefinitions.key` (string, required)
    Unique key within tenant+entityType (camelCase)

  - `included.requiredUserInfo.fieldDefinitions.label` (string, required)
    Human-readable label for form display

  - `included.requiredUserInfo.fieldDefinitions.description` (object)
    Help text / description

  - `included.requiredUserInfo.fieldDefinitions.fieldType` (string, required)
    Field type: text, email, phone, number, date, boolean, url, select, multiselect
    Enum: "text", "email", "phone", "number", "date", "boolean", "url", "select", "multiselect"

  - `included.requiredUserInfo.fieldDefinitions.selectOptions` (array)
    For select/multiselect types: static options array

  - `included.requiredUserInfo.fieldDefinitions.selectOptions.value` (string)

  - `included.requiredUserInfo.fieldDefinitions.selectOptions.label` (string)

  - `included.requiredUserInfo.fieldDefinitions.selectOptionsSource` (object)
    For select/multiselect types: dynamic options from entity

  - `included.requiredUserInfo.fieldDefinitions.validation` (object)
    Validation rules for the field

  - `included.requiredUserInfo.fieldDefinitions.sortOrder` (number, required)
    Display order (lower = first)

  - `included.requiredUserInfo.fieldDefinitions.isCustom` (boolean, required)
    Marker to distinguish from built-in fields. Custom fields are stored in user.customData

  - `included.requiredUserInfo.fieldDefinitions.createdAt` (string, required)
    Creation timestamp

  - `included.requiredUserInfo.fieldDefinitions.updatedAt` (string, required)
    Last update timestamp

  - `included.requiredUserInfo.isValid` (boolean)
    True if user has all required fields

  - `included.booking` (object)
    Booking validation status. Only set when bookingRequirement is set + user authenticated. (via ?include=booking)

  - `included.booking.isValid` (boolean)
    True if user has valid booking meeting requirements

## Response 201 fields (application/json):

  - `id` (string, required)
    Redemption id

  - `ownerBusinessId` (string, required)
    Optional owner business ID. If set, this redemption belongs to a specific business. If null, the redemption is tenant-owned (system-level).

  - `description` (string, required)
    Description of the redemption

  - `name` (string, required)
    Name of the redemption

  - `imageUrl` (string, required)
    Image url of the redemption

  - `logoUrl` (string, required)
    Logo URL for the redemption

  - `terms` (string, required)
    Terms and conditions for the redemption. Plain text or HTML. Multi-language handling at app level.

  - `startDate` (string, required)
    Redemption start date

  - `endDate` (string, required)
    Redemption end date

  - `isActive` (boolean, required)
    Redemption is active

  - `approval` (object, required)
    Approval metadata for this redemption.

  - `approval.status` (string, required)
    Approval state: pending_approval = awaiting admin review, approved = active, rejected = denied.
    Enum: "pending_approval", "approved", "rejected"

  - `approval.approvedAt` (string)
    Timestamp when the entity was approved or rejected by an admin.

  - `approval.approvedBy` (string)
    Admin user ID who approved or rejected this entity.

  - `approval.rejectionReason` (string)
    Reason provided when the entity was rejected.

  - `creditTokenAmount` (number, required)
    The amount of credit tokens needed to redeem the redemption tokens

  - `type` (object, required)
    The redemption type

  - `type.name` (string, required)
    Redemption type name

  - `type.description` (string)
    Redemption type description

  - `type.imageUrl` (string)
    Redemption type image url

  - `type.baseRequiredUserFields` (array)
    Base required user fields for all redemptions of this type. Can include built-in field keys (email, firstName) or custom field definition UUIDs.

  - `type.bookingRequirement` (object)
    Default booking requirement config for all redemptions of this type. Individual redemptions can override.

  - `type.bookingRequirement.type` (string, required)
    Type of booking required for eligibility.
    Enum: "active", "future", "past", "active_future", "any"

  - `type.bookingRequirement.eligibleBusinessIds` (array)
    Specific business IDs where booking must be. `null` = any business allowed, `[]` = no businesses allowed (blocks all). Combined with eligibleBusinessTypeIds via OR.

  - `type.bookingRequirement.eligibleBusinessTypeIds` (array)
    Business type IDs where booking must be. `null` = any type allowed, `[]` = no types allowed (blocks all). Combined with eligibleBusinessIds via OR.

  - `type.id` (number, required)
    Redemption type id

  - `priceTokenUnits` (array, required)
    Optional token units the user must transfer/burn as payment (ERC20, ERC1155, or ERC721). When set, these are transferred from user → business in addition to (or instead of) creditTokenAmount.

  - `priceTokenUnits.tokenEntityId` (string, required)
    Token contract entity UUID

  - `priceTokenUnits.tokenMetadataIncrementalId` (number)
    Explicit on-chain tokenId (ERC1155) or template lookup key (ERC721). Leave null when resolveByBusiness=true.

  - `priceTokenUnits.resolveByBusiness` (boolean)
    When true, the tokenMetadataIncrementalId is resolved at redeem time by looking up the TokenMetadata whose ownerBusinessId matches the redemption's ownerBusinessId. Requires the token to be a stampToken. Cannot be combined with an explicit tokenMetadataIncrementalId.

  - `priceTokenUnits.resolveTag` (string)
    Discriminator tag used when resolveByBusiness=true and a business has multiple stamp tokenIds on the same contract (e.g. "coffee", "meal"). Must match at least one tag on the resolved TokenMetadata. When omitted, a single unambiguous match is expected.

  - `priceTokenUnits.amount` (number, required)
    Amount of tokens the user must transfer as payment.

  - `mechanism` (string, required)
    Computed payment mechanism. Use this for rendering decisions (FREE = no payment, CREDIT = platform credit tokens, TOKEN_PAY = ERC20 transfer, TOKEN_BURN = ERC1155/ERC721 stamp/NFT, MIXED = credit + token). Never stored — derived from creditTokenAmount and priceTokenUnits.
    Enum: "FREE", "CREDIT", "TOKEN_PAY", "TOKEN_BURN", "MIXED"

  - `tokenUnits` (array, required)
    The redemption tokens to be redeemed

  - `tokenUnits.id` (string, required)
    Database UUID for this token unit

  - `tokenUnits.token` (object, required)
    The token contract this unit references

  - `tokenUnits.token.id` (string, required)
    Database UUID for this token contract entity

  - `tokenUnits.token.contractAddress` (string, required)
    Smart contract address deployed on the blockchain

  - `tokenUnits.token.metadata` (array)
    Token metadata templates - blueprints used when minting NFTs. Each template defines properties (name, image, expiry) and has a unique tokenMetadataIncrementalId. Null for ERC20 (Points) tokens.

  - `tokenUnits.token.metadata.ownerBusinessId` (string)
    Optional owner business ID. If set, this token metadata belongs to a specific business. If null, the token metadata is tenant-owned (system-level).

  - `tokenUnits.token.metadata.isActive` (boolean)
    Whether this template is active and can be used for minting

  - `tokenUnits.token.metadata.imageUrl` (string)
    This is the URL to the image of the item. Can be just about any type of image (including SVGs, which will be cached into PNGs by OpenSea), IPFS or Arweave URLs or paths. We recommend using a minimum 3000 x 3000 image.

  - `tokenUnits.token.metadata.externalUrl` (string)
    This is the URL that will appear below the asset's image

  - `tokenUnits.token.metadata.description` (string)
    A human-readable description of the item. Markdown is supported.
**ERC721 Template Interpolation:** Supports `{{placeholder}}` syntax for dynamic personalization at mint time (without AI costs).
**Available placeholders:**
- `{{user.firstName}}`, `{{user.lastName}}` - User data
- `{{campaign.name}}`, `{{business.displayName}}` - Campaign/business
- `{{context.xxx}}` - Dynamic context values (TriggerSource.context or claim request)
- `{{defaults.xxx}}` - Default values from defaultPromptContext

**Example:** `"Thank you {{user.firstName}} for visiting {{business.displayName}}! Your exclusive reward for {{context.eventName}}."`
**Processing order:** Template interpolation runs first, then AI processing (if configured) can further enhance the result.

  - `tokenUnits.token.metadata.name` (string)
    Name of the item.
**ERC721 Template Interpolation:** Supports `{{placeholder}}` syntax for dynamic personalization at mint time (without AI costs).
**Available placeholders:**
- `{{user.firstName}}`, `{{user.lastName}}` - User data
- `{{campaign.name}}`, `{{business.displayName}}` - Campaign/business
- `{{context.xxx}}` - Dynamic context values (TriggerSource.context or claim request)
- `{{defaults.xxx}}` - Default values from defaultPromptContext

**Example:** `"Welcome {{context.guestName}} - VIP Pass"` or `"{{user.firstName}}'s {{campaign.name}} Reward"`
**Processing order:** Template interpolation runs first, then AI processing (if configured) can further enhance the result.

  - `tokenUnits.token.metadata.validityType` (string)
    **ERC721 only** - Validity type defines how token expiry is calculated. Use with validityDuration for relative types. Ignored for ERC1155 tokens.
    Enum: "fixed_date", "days_from_issuance", "hours_from_issuance", "months_from_issuance", "end_of_month", "end_of_year", "trigger_date", "trigger_date_range", "days_from_trigger", "hours_from_trigger"

  - `tokenUnits.token.metadata.validityDuration` (number)
    **ERC721 only** - Duration in days or hours (used with days_from_issuance, hours_from_issuance validity types). Ignored for ERC1155 tokens.
    Example: 30

  - `tokenUnits.token.metadata.expiryDate` (string)
    Fixed expiry date (used with fixed_date validity type). For other validity types, this is computed at issuance time.

  - `tokenUnits.token.metadata.animationUrl` (string)
    A URL to a multi-media attachment for the item. The file extensions GLTF, GLB, WEBM, MP4, M4V, OGV, and OGG are supported, along with the audio-only extensions MP3, WAV, and OGA. Animation_url also supports HTML pages, allowing you to build rich experiences and interactive NFTs using JavaScript canvas, WebGL, and more. Scripts and relative paths within the HTML page are now supported. However, access to browser extensions is not supported.

  - `tokenUnits.token.metadata.youtubeUrl` (string)
    A URL to a YouTube video (only used if animation_url is not provide

  - `tokenUnits.token.metadata.creatorAccountAddress` (string)
    Creator Address

  - `tokenUnits.token.metadata.previewUrl` (string)
    Preview Url

  - `tokenUnits.token.metadata.tags` (array)
    Tags for categorization and filtering
    Example: ["summer","vip","limited-edition"]

  - `tokenUnits.token.metadata.aiPromptConfigs` (array)
    **ERC721 only** - AI prompt configurations for dynamic content generation at mint time.
AI processing runs AFTER template interpolation, so prompts can reference already-interpolated values.
Results are mapped by key to override static fields (name, description, imageUrl). Ignored for ERC1155 tokens.
**Note:** For simple personalization without AI costs, use `{{placeholder}}` syntax directly in name/description fields instead.
    Example: [{"type":"TEXT_GENERATION","key":"name","prompt":"Generate a unique reward name for {{user.firstName}} at {{business.displayName}}"},{"type":"IMAGE_GENERATION","key":"imageUrl","prompt":"A {{defaults.…

  - `tokenUnits.token.metadata.aiPromptConfigs.type` (string, required)
    Type of AI operation to perform
    Enum: "TEXT_GENERATION", "IMAGE_GENERATION"

  - `tokenUnits.token.metadata.aiPromptConfigs.key` (string, required)
    Key to store the generated result under
    Example: name

  - `tokenUnits.token.metadata.aiPromptConfigs.prompt` (string, required)
    The prompt to send to the AI model. Supports dynamic placeholders that are replaced with context values.
**Available Placeholders:**
- `{{user.firstName}}`, `{{user.lastName}}`, `{{user.email}}` - User profile data
- `{{campaign.name}}`, `{{campaign.description}}` - Campaign data
- `{{tenant.projectName}}`, `{{tenant.projectDescription}}` - Tenant/project data
- `{{business.displayName}}`, `{{business.name}}` - Business data
- `{{triggerSource.name}}`, `{{triggerSource.type}}` - Trigger source data
- `{{redemption.name}}`, `{{redemption.description}}` - Redemption data
- `{{context.xxx}}` - Dynamic context from TriggerSource.context or CampaignClaimRequest.context
- `{{defaults.xxx}}` - Default values from TokenMetadata.defaultPromptContext

**Context Sources:**
1. TriggerSource.context (admin QR/NFC) - Always applied
2. CampaignClaimRequest.context (user API) - Only if allowExternalContextOverwrite=true
3. TokenMetadata.defaultPromptContext - Always applied as fallback

**Examples:**
- `"Generate a welcome message for {{user.firstName}} at {{business.displayName}}"`
- `"Create a VIP badge for guest {{context.guestName}} in room {{context.roomNumber}}"`
- `"Design a {{defaults.style}} reward image for {{campaign.name}}"`
    Example: Generate a unique reward name for {{user.firstName}} visiting {{business.displayName}}

  - `tokenUnits.token.metadata.allowExternalContextOverwrite` (boolean)
    **ERC721 only** - Allow USER-PROVIDED external context from claim/redeem requests to be used in template interpolation and AI prompts.
**Security model:**
- `false` (default): Only admin-controlled context (TriggerSource.context, Redemption.context, defaultPromptContext) is available
- `true`: User-provided context from API requests is also merged and available as `{{context.xxx}}`

**Note:** Admin-controlled context (QR/NFC data) is ALWAYS applied regardless of this flag.
    Example: true

  - `tokenUnits.token.metadata.defaultPromptContext` (object)
    **ERC721 only** - Default context values for template interpolation and AI prompts. Available as `{{defaults.xxx}}` placeholders.
Always applied regardless of allowExternalContextOverwrite. Use for brand defaults, styling preferences, or fallback values.
**Example usage in name:** `"{{defaults.brand}} - {{user.firstName}}'s Reward"`
    Example: {"brand":"PERS Rewards","style":"minimal","defaultLocation":"Online"}

  - `tokenUnits.token.metadata.businessIds` (array)
    Business IDs where this token can be redeemed/spent. Empty array means any business in the tenant.
    Example: ["business-uuid-1","business-uuid-2"]

  - `tokenUnits.token.metadata.webhookId` (string)
    **ERC721 only** - Webhook ID for dynamic data fetch at mint time.
If set, the webhook is executed before metadata generation and response data is merged into the NFT.
**Use cases:**
- Fetch guest data from PMS/CRM systems
- Get real-time pricing or availability
- Retrieve user-specific content from external APIs

**Processing order:** Webhook fetch runs AFTER template interpolation but BEFORE AI processing.
    Example: webhook-uuid-for-pms-lookup

  - `tokenUnits.token.metadata.webhookPayloadTemplate` (object)
    **ERC721 only** - Payload template for webhook request.
Supports `{{placeholder}}` interpolation with same context as AI prompts.
**Example:**

```json
{
  "userId": "{{user.id}}",
  "campaignId": "{{campaign.id}}",
  "bookingId": "{{context.bookingId}}"
}
```
    Example: {"userId":"{{user.id}}","bookingId":"{{context.bookingId}}"}

  - `tokenUnits.token.metadata.webhookFieldMapping` (object)
    **ERC721 only** - Field mapping from webhook response to metadata fields.
Keys are webhook response paths (dot notation for nested), values are target field names.
**Mapping rules:**
- Explicit mapping: `"response.path": "targetField"`
- Known fields (name, description, imageUrl): auto-mapped if present
- Unknown fields or `"attributes"` target: become NFT attributes

**Example:**

```json
{
  "guestName": "name",
  "data.guest.image": "imageUrl",
  "roomNumber": "attributes",
  "checkInDate": "attributes"
}
```
If null/undefined, uses auto-mapping.
    Example: {"guestName":"name","roomNumber":"attributes","data.image":"imageUrl"}

  - `tokenUnits.token.metadata.consumable` (boolean)
    Whether this token template is consumable (burned on redemption) or collectible (transferred). Applies to ERC1155 and ERC721 only — ERC20 is always transferred. When true, the token is destroyed (burn) when used as payment in a redemption. Defaults to true — most token templates are consumable. Set to false for collectible/non-burning use cases. Mirrored as a consumable attribute in the on-chain IPFS metadata.

  - `tokenUnits.token.metadata.id` (string, required)
    Database UUID for this token metadata template

  - `tokenUnits.token.metadata.animationWeb3StorageUrl` (string)
    IPFS/Arweave URL for animation file - immutable web3 storage

  - `tokenUnits.token.metadata.imageWeb3StorageUrl` (string)
    IPFS/Arweave URL for image - immutable web3 storage

  - `tokenUnits.token.metadata.web3StorageUrl` (string)
    IPFS/Arweave URL for complete metadata JSON - this URL is stored on-chain and links to off-chain metadata

  - `tokenUnits.token.metadata.tokenMetadataIncrementalId` (number, required)
    Incremental ID within the token contract. For ERC1155: becomes the on-chain tokenId. For ERC721: lookup key for template to generate unique metadata.

  - `tokenUnits.token.metadata.approval` (object, required)
    Approval metadata for this token metadata.

  - `tokenUnits.token.metadata.mintCount` (number)
    Total number of mints for this token metadata (via ?include=mintCount). Counts SUCCEEDED MINT transactions using tokenMetadataIncrementalId. Accurate for both ERC1155 and ERC721 (post v2.3.49). Historical ERC721 transactions before v2.3.49 are not counted.

  - `tokenUnits.token.metadata.burnCount` (number)
    Total number of burns for this token metadata (via ?include=burnCount). Counts SUCCEEDED BURN transactions using tokenMetadataIncrementalId. Accurate for both ERC1155 and ERC721 (post v2.3.49). Historical ERC721 transactions before v2.3.49 are not counted.

  - `tokenUnits.token.metadata.included` (object)
    Included related entities. Only populated when include parameter is specified.

  - `tokenUnits.token.metadata.included.ownerBusiness` (object)

  - `tokenUnits.token.metadata.included.ownerBusiness.id` (string, required)
    The id of the business, this is unique and will be used to identify the business.

  - `tokenUnits.token.metadata.included.ownerBusiness.email` (string, required)
    The email of the business, this is unique and will be used to identify the business.

  - `tokenUnits.token.metadata.included.ownerBusiness.accountAddress` (string, required)
    The address of the business, this is the address that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.currentSigningAccountId` (string, required)
    Current active signing account ID for external wallet operations

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets` (array, required)
    Business-owned counterfactual smart contract wallets that can receive tokens

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.id` (string, required)
    Unique identifier for the internal wallet

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.ownerType` (string, required)
    Owner type for polymorphic ownership
    Enum: "user", "business", "tenant", "system", "external"

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.ownerId` (string, required)
    Owner ID for polymorphic ownership
    Example: user_123

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.walletManagementType` (string, required)
    Type of internal wallet
    Enum: "custodial", "non-custodial"

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.address` (string, required)
    CREATE2 generated address that can receive tokens

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.chainId` (number, required)
    Blockchain network chain identifier

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.status` (string, required)
    Current status of the wallet
    Enum: "pending", "active", "suspended", "archived"

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.ownerSigningAccountId` (string)
    ID of signing account that owns this internal wallet

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.tags` (array, required)
    Tags associated with the wallet for categorization

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.createdAt` (string, required)
    Timestamp when the wallet was created

  - `tokenUnits.token.metadata.included.ownerBusiness.wallets.updatedAt` (string, required)
    Timestamp when the wallet was last updated

  - `tokenUnits.token.metadata.included.ownerBusiness.businessLegalName` (string, required)
    The legal name of the business, this is the name that will be used for legal purposes.

  - `tokenUnits.token.metadata.included.ownerBusiness.registrationNumber` (string, required)
    The business registration number (e.g., company registration, VAT number, EIN)

  - `tokenUnits.token.metadata.included.ownerBusiness.displayName` (string, required)
    The display name of the business, this is the name that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.description` (string, required)
    The description of the business, this is the description that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.shortDescription` (string, required)
    The short description of the business, this is the description that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.websiteUrl` (string, required)
    The website of the business, this is the website that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.imageUrl` (string, required)
    The image of the business, this is the image that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.logoUrl` (string, required)
    Logo URL for the business

  - `tokenUnits.token.metadata.included.ownerBusiness.streetAddress` (string, required)
    The address of the business, this is the address that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.phoneNumber` (string, required)
    The phone number of the business.

  - `tokenUnits.token.metadata.included.ownerBusiness.neighborhood` (string, required)
    Neighborhood/area name (e.g., "West Bay", "Pearl Qatar", "Lusail") - auto-populated from geocoding

  - `tokenUnits.token.metadata.included.ownerBusiness.district` (string, required)
    District/administrative area - auto-populated from geocoding

  - `tokenUnits.token.metadata.included.ownerBusiness.city` (string, required)
    The city of the business, this is the city that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.postalCode` (string, required)
    The postal code of the business, this is the postal code that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.country` (string, required)
    The country of the business (auto-populated from geocoding if coordinates provided)

  - `tokenUnits.token.metadata.included.ownerBusiness.countryCode` (string, required)
    ISO 3166-1 alpha-2 country code (e.g., QA, US, AE) - auto-populated from geocoding
    Example: QA

  - `tokenUnits.token.metadata.included.ownerBusiness.coordsLatitude` (number, required)
    The latitude of the business, this is the latitude that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.coordsLongitude` (number, required)
    The longitude of the business, this is the longitude that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.businessType` (object, required)
    The business type of the business, this is the business type that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.businessType.id` (string)
    Business type id

  - `tokenUnits.token.metadata.included.ownerBusiness.businessType.name` (string, required)
    Business type name

  - `tokenUnits.token.metadata.included.ownerBusiness.businessType.description` (string, required)
    Business type description

  - `tokenUnits.token.metadata.included.ownerBusiness.businessType.code` (string, required)
    Business type code

  - `tokenUnits.token.metadata.included.ownerBusiness.businessType.iconUrl` (string, required)
    Business type icon url

  - `tokenUnits.token.metadata.included.ownerBusiness.isActive` (boolean, required)
    The status of the business, this is the status that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.approval` (object, required)
    Approval metadata for this business.

  - `tokenUnits.token.metadata.included.ownerBusiness.canMintToken` (boolean, required)
    The ability to mint token for the business, this is the ability that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.canChargeToken` (boolean, required)
    The ability to charge token for the business, this is the ability that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.canManageUsers` (boolean, required)
    The ability to manage users for the business, this is the ability that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.canReceiveDonation` (boolean, required)
    The ability to receive donation for the business, this is the ability that will be shown to the public.

  - `tokenUnits.token.metadata.included.ownerBusiness.tags` (array, required)
    Tags for categorization and filtering

  - `tokenUnits.token.metadata.included.ownerBusiness.customData` (object, required)
    Custom business data including Google Places information (placeID, currentOpeningHours)
    Example: {"placeID":"ChIJN1t_tDeuEmsRUsoyG83frY4","currentOpeningHours":{"open_now":true,"weekday_text":["Monday: 9:00 AM – 5:00 PM","Tuesday: 9:00 AM – 5:00 PM"]}}

  - `tokenUnits.token.metadata.included.token` (object)
    Parent token contract info (via ?include=token)

  - `tokenUnits.token.metadata.included.token.chainId` (number)
    Blockchain chain ID (mainnet or testnet)

  - `tokenUnits.token.metadata.included.token.contractAddress` (string)
    Smart contract address

  - `tokenUnits.token.metadata.included.token.type` (string)
    Token type (ERC721, ERC1155)

  - `tokenUnits.token.abi` (object, required)
    this is the abi of the contract, this is the interface of the contract to interact with it

  - `tokenUnits.token.chainId` (number, required)
    this is the chain id of the chain where the token is deployed

  - `tokenUnits.token.abiUrl` (string, required)
    this is the url of the abi of the contract, to be used to fetch the abi of the contract

  - `tokenUnits.token.name` (string)
    this is the name of the token contract

  - `tokenUnits.token.symbol` (string)
    this is the symbol of the token contract, this is optional and can be null

  - `tokenUnits.token.decimals` (number)
    this is the decimals of the token. This is optional and only used for ERC20 tokens (Points)

  - `tokenUnits.token.isActive` (boolean, required)
    This can be used to enable or disable the token

  - `tokenUnits.token.isGallery` (boolean, required)
    This can be used to enable or disable the token for gallery

  - `tokenUnits.token.type` (string, required)
    This is the underlying web3 native type of the token contract
    Enum: "ERC20", "ERC1155", "ERC721"

  - `tokenUnits.token.stampToken` (boolean, required)
    When true, this ERC1155 contract is used as a shared stamp contract. TokenMetadata entries are auto-provisioned per business and can be referenced in redemption priceTokenUnits with resolveByBusiness=true.

  - `tokenUnits.tokenMetadataIncrementalId` (number)
    Token metadata template ID. For ERC1155: becomes on-chain tokenId. For ERC721: lookup key for template to generate unique metadata.

  - `tokenUnits.amount` (number, required)
    Amount of tokens to mint/transfer. For ERC721 this is typically 1, for ERC1155/ERC20 can be any quantity.

  - `tokenUnits.maxAmount` (number)
    Token unit max amount. Applies to MINT/EARN contexts only (e.g. campaign reward capping where user earns tokens per revenue spent). Ignored for spend/transfer contexts such as redemption priceTokenUnits.

  - `order` (number, required)
    The order of the redemption

  - `supply` (object)
    The available supply of the redemption

  - `supply.available` (number, required)
    Number of items available for supply. Null if unlimited.

  - `supply.total` (number, required)
    Total number of items for supply. Null if unlimited.

  - `maxPerUser` (number, required)
    The maximum redemption per user

  - `minUserStatusType` (object, required)
    The minimum user status type to redeem the redemption

  - `minUserStatusType.name` (string, required)
    User Status Type name

  - `minUserStatusType.description` (string)
    User Status Type description

  - `minUserStatusType.minTokenBalance` (string, required)
    User Status Type eligible Token Addresses

  - `minUserStatusType.discountPercentage` (number, required)
    User Status Type discount Rate in percentage

  - `minUserStatusType.imageUrl` (string)
    User Status Type image Url

  - `minUserStatusType.eligibleTokenAddresses` (array)
    Eligible token contract addresses for this status type
    Example: ["0x1234...","0x5678..."]

  - `minUserStatusType.tags` (array)
    Tags for categorization and filtering
    Example: ["vip","premium","gold"]

  - `minUserStatusType.order` (number)
    Explicit ordering for status hierarchy (higher = more prestigious). If not set, falls back to minTokenBalance for ordering.
    Example: 100

  - `minUserStatusType.id` (number, required)
    User Status Type id

  - `countryCodeRestrictions` (object)
    Country code restrictions as an array of strings (e.g., ["NOT_ES", "FR"])
    Example: ["NOT_ES","FR"]

  - `tags` (array, required)
    Redemption tags

  - `context` (object)
    **ERC721 only** - Admin-defined dynamic context for AI prompts and trigger-based validity. Always applied to tokens minted from this redemption. Special keys: `validityDate`, `validityEndDate`, `validityDuration` for token expiry.
    Example: {"eventName":"Summer Sale","validityDuration":30}

  - `specificRequiredUserFields` (array, required)
    Required user fields specific to this redemption (beyond type defaults). Can include built-in field keys (email, firstName) or custom field UUIDs.

  - `requiredUserFields` (array, required)
    All required fields for this redemption (type baseline + extras, deduplicated). Use this for validation.

  - `specificBookingRequirement` (object)
    Redemption-specific booking requirement override. Set to override type default. Omit or null = use type default.

  - `bookingRequirement` (object)
    Effective booking requirement (specific override ?? type default). Use this for validation. Null = no booking required.

  - `included` (object)
    Optional data requested via include parameter. All include-based data lives here for consistency.

  - `included.redeemCount` (number)
    Total number of redeems for this redemption (via ?include=redeemCount)

  - `included.requiredUserInfo` (object)
    Required user info validation status. Only set when requiredUserFields is non-empty + user authenticated. (via ?include=requiredUserInfo)

  - `included.requiredUserInfo.fieldDefinitions` (array)
    Field definitions with isMissing status

  - `included.requiredUserInfo.fieldDefinitions.id` (string, required)
    Unique identifier (UUID)

  - `included.requiredUserInfo.fieldDefinitions.tenantId` (string, required)
    Tenant ID

  - `included.requiredUserInfo.fieldDefinitions.entityType` (string, required)
    Entity type this field applies to
    Enum: "user", "business", "campaign", "redemption"

  - `included.requiredUserInfo.fieldDefinitions.key` (string, required)
    Unique key within tenant+entityType (camelCase)

  - `included.requiredUserInfo.fieldDefinitions.label` (string, required)
    Human-readable label for form display

  - `included.requiredUserInfo.fieldDefinitions.description` (object)
    Help text / description

  - `included.requiredUserInfo.fieldDefinitions.fieldType` (string, required)
    Field type: text, email, phone, number, date, boolean, url, select, multiselect
    Enum: "text", "email", "phone", "number", "date", "boolean", "url", "select", "multiselect"

  - `included.requiredUserInfo.fieldDefinitions.selectOptions` (array)
    For select/multiselect types: static options array

  - `included.requiredUserInfo.fieldDefinitions.selectOptions.value` (string)

  - `included.requiredUserInfo.fieldDefinitions.selectOptions.label` (string)

  - `included.requiredUserInfo.fieldDefinitions.selectOptionsSource` (object)
    For select/multiselect types: dynamic options from entity

  - `included.requiredUserInfo.fieldDefinitions.validation` (object)
    Validation rules for the field

  - `included.requiredUserInfo.fieldDefinitions.sortOrder` (number, required)
    Display order (lower = first)

  - `included.requiredUserInfo.fieldDefinitions.isCustom` (boolean, required)
    Marker to distinguish from built-in fields. Custom fields are stored in user.customData

  - `included.requiredUserInfo.fieldDefinitions.createdAt` (string, required)
    Creation timestamp

  - `included.requiredUserInfo.fieldDefinitions.updatedAt` (string, required)
    Last update timestamp

  - `included.requiredUserInfo.isValid` (boolean)
    True if user has all required fields

  - `included.booking` (object)
    Booking validation status. Only set when bookingRequirement is set + user authenticated. (via ?include=booking)

  - `included.booking.isValid` (boolean)
    True if user has valid booking meeting requirements

