Docs/Build guides/Redeem
Redeem
Prepare and store a signed redemption that offramps tokenized loyalty value back into a brand-defined benefit.
Create a redemption when the customer uses tokenized loyalty value for something the brand fulfills: a discount, product, upgrade, access pass, or other offchain benefit.
Inputs#
| Field | Type | Required | Meaning |
|---|---|---|---|
issuer | address | yes | Brand wallet that signs the redemption. |
from | address | yes | Customer wallet whose tokenized loyalty balance is redeemed. |
loyaltyId | bytes32 | yes | Brand-scoped program ID. |
amount | uint256 string | yes | Tokenized loyalty amount to redeem. Must be greater than zero. |
expiresAt | uint256 string | yes | The exact bucket to redeem. Use 0 for non-expiring rewards. |
deadline | uint256 string | yes | Last Unix timestamp when the redemption can be submitted. Use 0 for no execution deadline. |
nonce | bytes32 | yes | Unique per issuer. Replays are rejected. |
chainId | uint256 string | yes | Expected chain ID. Production uses Base mainnet 8453; non-production uses Base Sepolia 84532. |
verifyingContract | address | yes | The Loyfin factory that will execute the action. |
operationHash | bytes32 | optional | Brand reference hash for correlating this action with an internal database row. Defaults to zero bytes when omitted. |
data | hex bytes | optional | Brand-defined fulfillment data. Defaults to 0x. Max 2048 bytes. |
Request#
{
"redemption": {
"issuer": "0x1111111111111111111111111111111111111111",
"from": "0x2222222222222222222222222222222222222222",
"loyaltyId": "0x4242424242424242424242424242424242424242424242424242424242424242",
"amount": "250",
"expiresAt": "0",
"deadline": "0",
"nonce": "0x7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a",
"chainId": "8453",
"verifyingContract": "0x3333333333333333333333333333333333333333",
"operationHash": "0x9999999999999999999999999999999999999999999999999999999999999999",
"data": "0x"
},
"signature": "0xababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab"
}curl https://api.loyfin.com/redemptions \
-X POST \
-H "content-type: application/json" \
-d '{
"redemption": {
"issuer": "0x1111111111111111111111111111111111111111",
"from": "0x2222222222222222222222222222222222222222",
"loyaltyId": "0x4242424242424242424242424242424242424242424242424242424242424242",
"amount": "250",
"expiresAt": "0",
"deadline": "0",
"nonce": "0x7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a",
"chainId": "8453",
"verifyingContract": "0x3333333333333333333333333333333333333333",
"operationHash": "0x9999999999999999999999999999999999999999999999999999999999999999",
"data": "0x"
},
"signature": "0xababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab"
}'const response = await fetch("https://api.loyfin.com/redemptions", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ redemption, signature })
});
if (!response.ok) {
throw new Error(await response.text());
}
const { operation, duplicate } = await response.json();Fulfillment#
Loyfin burns the signed tokenized amount from the customer's selected bucket. The issuer only needs to publish the signed redemption; Loyfin's official relayer or an independent relayer can submit it. Your brand system should fulfill the promised benefit when the redemption is completed.
Common errors#
- Customer has insufficient balance in the selected expiry bucket.
- The bucket is expired.
- The action deadline has passed.
- The signature, nonce, chain, or verifying contract does not match.
Brand database flow#
Treat redemption as a pending brand operation until it is confirmed.
A redemption usually starts inside the brand app: the customer chooses a tokenized reward to use, the app calls your brand API, your backend creates an internal redemption row, signs the redemption, and stores it through Loyfin or submits it onchain directly. The brand database remains the system of record for fulfillment, support, and customer-facing order state.
- Use operationHash to connect the signed redemption, indexed Loyfin activity, and your internal redemption row.
- After
POST /redemptionsreturns successfully, keep the brand operation pending until the redemption is confirmed. - On confirmation, apply the brand-side result: mark the benefit fulfilled, update the order or campaign record, and show the customer the completed redemption.
- The helper API is the default path for performance and efficiency. Confirmation can come from polling
GET /operationsorGET /redemptions, future issuer webhooks, or direct onchain event reads. Brands can also submit theredeemtransaction themselves instead of relying on the helper API.
State machine#
Fulfillment should wait for the tokenized value to be burned.
Requested
The customer starts redemption in the brand app, checkout, support flow, or campaign surface.
Authorized
Your backend checks the intended benefit, customer identity, token bucket, and internal support/order context.
Created
POST the signed redemption to Loyfin and keep the benefit pending in your own system.
Fulfilled
After the redemption is mined, fulfill the benefit and store the receipt link for support.