Docs/Build guides/Redeem

Redeem

Prepare and store a signed redemption that offramps tokenized loyalty value back into a brand-defined benefit.

GoalOfframp tokenized loyalty value into a 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#

FieldTypeRequiredMeaning
issueraddressyesBrand wallet that signs the redemption.
fromaddressyesCustomer wallet whose tokenized loyalty balance is redeemed.
loyaltyIdbytes32yesBrand-scoped program ID.
amountuint256 stringyesTokenized loyalty amount to redeem. Must be greater than zero.
expiresAtuint256 stringyesThe exact bucket to redeem. Use 0 for non-expiring rewards.
deadlineuint256 stringyesLast Unix timestamp when the redemption can be submitted. Use 0 for no execution deadline.
noncebytes32yesUnique per issuer. Replays are rejected.
chainIduint256 stringyesExpected chain ID. Production uses Base mainnet 8453; non-production uses Base Sepolia 84532.
verifyingContractaddressyesThe Loyfin factory that will execute the action.
operationHashbytes32optionalBrand reference hash for correlating this action with an internal database row. Defaults to zero bytes when omitted.
datahex bytesoptionalBrand-defined fulfillment data. Defaults to 0x. Max 2048 bytes.

Request#

Redemption bodyjson
{
  "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"
}
POST /redemptionsbash
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"
}'
TypeScripttypescript
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 /redemptions returns 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 /operations or GET /redemptions, future issuer webhooks, or direct onchain event reads. Brands can also submit the redeem transaction themselves instead of relying on the helper API.

State machine#

Fulfillment should wait for the tokenized value to be burned.

01

Requested

The customer starts redemption in the brand app, checkout, support flow, or campaign surface.

02

Authorized

Your backend checks the intended benefit, customer identity, token bucket, and internal support/order context.

03

Created

POST the signed redemption to Loyfin and keep the benefit pending in your own system.

04

Fulfilled

After the redemption is mined, fulfill the benefit and store the receipt link for support.