Perk Charges & Redemption API
Track and manage per-NFT usage charges for claimed perks. When a customer claims a perk NFT, it starts with a set number of charges. Your backend calls this API to decrement charges each time the perk is redeemed (e.g., scanning a QR code, presenting at checkout).
These endpoints use HMAC signature authentication with your existing Brand ID and Security Key from the Partner Portal. No additional API keys required.
Overview
Pattern: Brand-authenticated charge management
- Customer claims a perk NFT (charges initialized automatically)
- Customer presents perk at your business (QR code, membership card, etc.)
- Your backend calls
POST /redeem-perkwith HMAC signature - Charges decrement; remaining count returned
- NFT metadata updates to reflect current charge state
Use Cases:
- Multi-visit coffee cards (10 charges = 10 coffees)
- Monthly subscription perks (12 charges = 12 months)
- Event access passes (3 charges = 3 events)
- Unlimited-use membership cards (0 charges = unlimited)
Base URL
https://relayer.rsnc.network
Authentication
All perk charges endpoints require HMAC signature authentication using three headers:
X-Resonance-Brand-Id: 0xYourBrandWalletAddress
X-Resonance-Signature: <HMAC-SHA256 signature>
X-Resonance-Timestamp: <Unix timestamp in seconds>
How HMAC Signing Works
- Get your Brand ID (wallet address) and Security Key from Partner Portal → Settings → Profile
- Construct the signing payload:
BrandId|RequestBody|Timestamp - Compute
HMAC-SHA256(payload, SecurityKey)and send as hex in the signature header
Signatures are valid for 5 minutes. Requests with timestamps older than 5 minutes are rejected.
Signing Example
# Your credentials (from Partner Portal → Settings → Profile)
BRAND_ID="0xYourBrandWalletAddress"
SECURITY_KEY="your-64-char-hex-security-key"
# Request details
TIMESTAMP=$(date +%s)
BODY='{"token_id":42,"collection_id":7,"charges_to_use":1}'
# Compute HMAC-SHA256
PAYLOAD="${BRAND_ID}|${BODY}|${TIMESTAMP}"
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECURITY_KEY" | awk '{print $2}')
Signing for GET Requests
For GET requests (no body), the payload is: BrandId||Timestamp
PAYLOAD="${BRAND_ID}||${TIMESTAMP}"
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECURITY_KEY" | awk '{print $2}')
Endpoints
POST /redeem-perk
Decrement charges on a specific perk NFT token. Call this each time a customer redeems their perk at your business.
Headers:
Content-Type: application/json
X-Resonance-Brand-Id: 0xYourBrandWalletAddress
X-Resonance-Signature: <HMAC-SHA256 hex>
X-Resonance-Timestamp: <Unix seconds>
Request Body:
{
"token_id": 42,
"collection_id": 7,
"charges_to_use": 1,
"notes": "Redeemed at downtown location"
}
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
token_id | integer | Yes | The NFT token ID (from the customer's wallet) |
collection_id | integer | Yes | Your perk collection ID |
charges_to_use | integer | No | Number of charges to deduct (default: 1) |
notes | string | No | Optional note for audit log (e.g., location, staff ID) |
Success Response (200):
{
"success": true,
"token_id": 42,
"collection_id": 7,
"total_charges": 10,
"used_charges": 3,
"remaining": 7
}
No Charges Remaining (409):
{
"error": "No charges remaining",
"token_id": 42,
"collection_id": 7,
"total_charges": 10,
"used_charges": 10,
"remaining": 0
}
Unlimited Charges: When total_charges is 0, the perk has unlimited uses. The response returns "remaining": "unlimited" and redemptions always succeed.
curl Example
BRAND_ID="0xYourBrandWalletAddress"
SECURITY_KEY="your-64-char-hex-security-key"
TIMESTAMP=$(date +%s)
BODY='{"token_id":42,"collection_id":7,"charges_to_use":1,"notes":"Downtown store"}'
PAYLOAD="${BRAND_ID}|${BODY}|${TIMESTAMP}"
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECURITY_KEY" | awk '{print $2}')
curl -X POST https://relayer.rsnc.network/redeem-perk \
-H "Content-Type: application/json" \
-H "X-Resonance-Brand-Id: ${BRAND_ID}" \
-H "X-Resonance-Signature: ${SIGNATURE}" \
-H "X-Resonance-Timestamp: ${TIMESTAMP}" \
-d "${BODY}"
GET /check-perk-status
Query the current charge status for a specific perk NFT token. Use this to check whether a perk is still valid before redeeming.
Headers:
X-Resonance-Brand-Id: 0xYourBrandWalletAddress
X-Resonance-Signature: <HMAC-SHA256 hex>
X-Resonance-Timestamp: <Unix seconds>
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
token_id | integer | Yes | The NFT token ID |
collection_id | integer | Yes | Your perk collection ID |
Success Response (200):
{
"token_id": 42,
"collection_id": 7,
"total_charges": 10,
"used_charges": 3,
"remaining": 7,
"last_redeemed_at": "2025-06-15T14:30:00Z"
}
Never-redeemed token (200):
{
"token_id": 42,
"collection_id": 7,
"total_charges": 10,
"used_charges": 0,
"remaining": 10,
"last_redeemed_at": null
}
curl Example
BRAND_ID="0xYourBrandWalletAddress"
SECURITY_KEY="your-64-char-hex-security-key"
TIMESTAMP=$(date +%s)
PAYLOAD="${BRAND_ID}||${TIMESTAMP}"
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECURITY_KEY" | awk '{print $2}')
curl -G "https://relayer.rsnc.network/check-perk-status" \
--data-urlencode "token_id=42" \
--data-urlencode "collection_id=7" \
-H "X-Resonance-Brand-Id: ${BRAND_ID}" \
-H "X-Resonance-Signature: ${SIGNATURE}" \
-H "X-Resonance-Timestamp: ${TIMESTAMP}"
GET /list-perk-holders
List all holders and their charge status for one of your perk collections. Useful for dashboards showing who has claimed your perks and how many charges remain.
Headers:
X-Resonance-Brand-Id: 0xYourBrandWalletAddress
X-Resonance-Signature: <HMAC-SHA256 hex>
X-Resonance-Timestamp: <Unix seconds>
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
collection_id | integer | Yes | Your perk collection ID |
Success Response (200):
{
"collection_id": 7,
"charges_per_nft": 10,
"total_tokens": 3,
"claimants": [
{
"address": "0xCustomer1Address...",
"claim_count": 2
},
{
"address": "0xCustomer2Address...",
"claim_count": 1
}
],
"holders": [
{
"token_id": 42,
"total_charges": 10,
"used_charges": 3,
"remaining": 7,
"last_redeemed_at": "2025-06-15T14:30:00Z",
"minted_at": "2025-06-01T10:00:00Z"
},
{
"token_id": 43,
"total_charges": 10,
"used_charges": 0,
"remaining": 10,
"last_redeemed_at": null,
"minted_at": "2025-06-02T12:00:00Z"
}
]
}
curl Example
BRAND_ID="0xYourBrandWalletAddress"
SECURITY_KEY="your-64-char-hex-security-key"
TIMESTAMP=$(date +%s)
PAYLOAD="${BRAND_ID}||${TIMESTAMP}"
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECURITY_KEY" | awk '{print $2}')
curl -G "https://relayer.rsnc.network/list-perk-holders" \
--data-urlencode "collection_id=7" \
-H "X-Resonance-Brand-Id: ${BRAND_ID}" \
-H "X-Resonance-Signature: ${SIGNATURE}" \
-H "X-Resonance-Timestamp: ${TIMESTAMP}"
Setting Charges Per Perk
When creating a perk in the Partner Portal, set the "Uses per perk" field:
| Value | Behavior |
|---|---|
1 | Single-use (default). Perk can be redeemed once. |
N | Multi-use. Each NFT gets exactly N redemptions. |
0 | Unlimited. Perk can be redeemed any number of times. |
This value is stored as charges_per_nft on the collection. When a customer claims the perk, a charge tracking row is automatically created with the correct total.
NFT Metadata Integration
Charge state is reflected in the NFT's ERC-721 metadata (visible on block explorers, wallets, and marketplaces):
{
"name": "Coffee Card #42",
"description": "10-visit coffee card from Bean Co.",
"image": "https://...",
"attributes": [
{ "trait_type": "Collection", "value": "Coffee Card" },
{ "trait_type": "Brand", "value": "Bean Co." },
{ "trait_type": "Total Charges", "value": "10" },
{ "trait_type": "Charges Used", "value": "3" },
{ "trait_type": "Charges Remaining", "value": "7" }
]
}
NFT metadata is cached for up to 5 minutes. After redeeming a charge, the updated metadata may take a few minutes to appear on block explorers.
Error Codes
| Status | Meaning | Description |
|---|---|---|
| 200 | Success | Operation completed |
| 400 | Bad Request | Missing or invalid parameters |
| 401 | Unauthorized | HMAC signature invalid, expired, or missing Brand ID |
| 403 | Forbidden | Brand does not own this collection |
| 404 | Not Found | Collection not found |
| 409 | Conflict | No charges remaining on this token |
| 500 | Server Error | Internal error |
Integration Patterns
Point-of-Sale Redemption
Your POS system verifies and redeems a perk when a customer presents it:
# 1. Customer presents perk (token_id from their wallet/QR code)
# 2. Check if perk is valid
curl -G "https://relayer.rsnc.network/check-perk-status" \
--data-urlencode "token_id=42" \
--data-urlencode "collection_id=7" \
-H "X-Resonance-Brand-Id: ${BRAND_ID}" \
-H "X-Resonance-Signature: ${SIGNATURE}" \
-H "X-Resonance-Timestamp: ${TIMESTAMP}"
# 3. If remaining > 0, redeem
curl -X POST "https://relayer.rsnc.network/redeem-perk" \
-H "Content-Type: application/json" \
-H "X-Resonance-Brand-Id: ${BRAND_ID}" \
-H "X-Resonance-Signature: ${SIGNATURE}" \
-H "X-Resonance-Timestamp: ${TIMESTAMP}" \
-d '{"token_id":42,"collection_id":7,"charges_to_use":1,"notes":"Store #5, register 2"}'
Webhook-Triggered Redemption
Automatically redeem when an external event occurs (e.g., subscription renewal):
# Called by your subscription webhook handler
curl -X POST "https://relayer.rsnc.network/redeem-perk" \
-H "Content-Type: application/json" \
-H "X-Resonance-Brand-Id: ${BRAND_ID}" \
-H "X-Resonance-Signature: ${SIGNATURE}" \
-H "X-Resonance-Timestamp: ${TIMESTAMP}" \
-d '{"token_id":42,"collection_id":7,"charges_to_use":1,"notes":"Monthly subscription renewal - July 2025"}'
Dashboard: List All Holders
Build an internal dashboard showing all perk holders and their usage:
curl -G "https://relayer.rsnc.network/list-perk-holders" \
--data-urlencode "collection_id=7" \
-H "X-Resonance-Brand-Id: ${BRAND_ID}" \
-H "X-Resonance-Signature: ${SIGNATURE}" \
-H "X-Resonance-Timestamp: ${TIMESTAMP}"
Security
Ownership Verification
All endpoints verify that your Brand ID (wallet address) matches the creator_address on the perk collection. You can only manage charges for collections you created.
Audit Trail
Every redemption is logged in an immutable perk_redemption_log with:
- Token ID and collection ID
- Brand ID that performed the redemption
- Number of charges used
- Timestamp
- Optional notes
Signature Replay Protection
- Timestamps must be within 5 minutes of server time
- Each signature is unique to the request body and timestamp
Best Practices
- Check before redeeming - Call
/check-perk-statusbefore/redeem-perkto show the customer their remaining charges and avoid unnecessary 409 errors - Include notes - Add location, staff ID, or order number to the
notesfield for audit tracking - Handle 409 gracefully - When charges are exhausted, show the customer a clear "perk fully redeemed" message
- Use unlimited (0) for memberships - Set charges to 0 for ongoing membership cards that never expire
- Monitor with list-perk-holders - Periodically check holder status to identify customers nearing exhaustion for re-engagement campaigns
Related Documentation
- Gas Relayer API - Gasless transaction relay (used for perk claiming)
- Perks Service - Perk browsing and platform redemption
- Building a Perk Claim Frontend - Consumer-facing perk marketplace
- Authentication Guide - Privy authentication for end users
Need Help?