curl --request POST \
--url https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"reward_id": 123,
"customer_merchant_id": "<string>",
"fulfill_immediately": true
}
'import requests
url = "https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem"
payload = {
"reward_id": 123,
"customer_merchant_id": "<string>",
"fulfill_immediately": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({reward_id: 123, customer_merchant_id: '<string>', fulfill_immediately: true})
};
fetch('https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'reward_id' => 123,
'customer_merchant_id' => '<string>',
'fulfill_immediately' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem"
payload := strings.NewReader("{\n \"reward_id\": 123,\n \"customer_merchant_id\": \"<string>\",\n \"fulfill_immediately\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"reward_id\": 123,\n \"customer_merchant_id\": \"<string>\",\n \"fulfill_immediately\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"reward_id\": 123,\n \"customer_merchant_id\": \"<string>\",\n \"fulfill_immediately\": true\n}"
response = http.request(request)
puts response.read_body{
"fulfillment_explanation": "<string>",
"claimed_reward": {
"id": 123,
"claimed_at": "<string>",
"reward_id": 123,
"title": "$5 voucher",
"state": "approved",
"source": {
"kind": "points_redemption",
"points_cost": 123
},
"auto_refund_at": "<string>",
"redeemable": {
"usage_status": "not_used",
"kind": "custom",
"fulfilment_explanation": "<string>",
"fulfilled_at": "<string>",
"code": "<string>"
}
},
"customer": {
"state": "enrolled",
"id": 123,
"merchant_id": "<string>",
"email": "<string>",
"first_name": "<string>",
"last_name": "<string>",
"points_approved": 123,
"points_pending": 123,
"points_spent": 123,
"enrolled_at": "<string>",
"tier_membership": {
"tier_id": 123,
"started_at": "2025-01-01T12:00:00Z",
"expires_at": "2026-01-01T12:00:00Z",
"progress": {
"kind": "points",
"valid_until": "<string>",
"points_needed_for_renewal": 123,
"upgrade_tier_id": 123,
"points_needed_for_upgrade": 123,
"tier_points": {
"now": 123,
"at_expiration": 123
},
"forecasted_tier_id": 123
}
},
"birthday": {
"month": 6,
"day": 25
},
"next_birthday": "2025-06-25",
"claimed_rewards": [
{
"id": 123,
"claimed_at": "<string>",
"reward_id": 123,
"title": "$5 voucher",
"state": "approved",
"source": {
"kind": "points_redemption",
"points_cost": 123
},
"auto_refund_at": "<string>",
"redeemable": {
"usage_status": "not_used",
"kind": "gift_card",
"code_last_characters": "<string>",
"initial_balance": {
"amount": 123,
"formatted": "<string>"
},
"current_balance": {
"amount": 123,
"formatted": "<string>"
},
"expires_at": "<string>"
}
}
],
"available_rewards": [
{
"id": 123,
"limit": {
"count": 123,
"interval": "day"
},
"kind": "gift_card",
"properties": {
"initial_balance": {
"amount": 123,
"formatted": "<string>"
}
},
"variant": {
"tier_id": 123,
"title": "$5 voucher",
"description": "Spend your points on $5 off your next order",
"enabled": true,
"cost": {
"kind": "fixed",
"points": 123
},
"cost_text": "100 points"
},
"context": {
"can_redeem": {
"state": "redeemable"
},
"last_claimed_at": "<string>",
"claim_count": 123,
"claim_limit": {
"state": "no_limit"
}
},
"image_url": "<string>"
}
],
"available_rules": [
{
"id": 123,
"limit": {
"count": 123,
"interval": "day"
},
"kind": "birthday",
"variant": {
"tier_id": 123,
"title": "Make a purchase",
"description": "Earn points every time you shop with us",
"enabled": true,
"result": {
"kind": "points",
"per_currency_unit": 1,
"points": 5
},
"result_short_text": "5 points per $1"
},
"context": {
"last_completed_at": "<string>",
"completion_count": 123,
"completion_limit": {
"state": "no_limit"
}
},
"image_url": "<string>"
}
],
"history": [
{
"kind": "earned_points_from_rule",
"label": "Join our program",
"date": "<string>",
"points": 1,
"points_text": "500 points",
"rule": {
"id": 123,
"kind": "birthday"
},
"points_will_approve_at": "<string>",
"points_will_expire_at": "<string>",
"state": "pending"
}
],
"active_cart_redemptions": [
{
"id": "<string>",
"claimed_reward_id": 123,
"expires_at": "<string>",
"product": {
"id": "<string>",
"handle": "<string>",
"title": "<string>",
"url": "<string>",
"image_url": "<string>",
"variant": {
"id": "<string>",
"title": "<string>",
"inventory_count": 123
}
},
"quantity": 123,
"cart_id": "<string>",
"cart_line": {
"kind": "shopify",
"merchandise_id": "gid://shopify/ProductVariant/1001",
"product_id": "gid://shopify/Product/100",
"quantity": 1,
"attributes": [
{
"key": "__lion_sfp_id",
"value": "19fdf4cb"
}
]
},
"current_cart_state": {
"in_cart": true,
"cart_requirements_met": true,
"cart_requirements_detail": {
"minimum_cart_total": {
"state": "no_requirement"
},
"minimum_line_item_quantity": {
"state": "no_requirement"
}
}
}
}
],
"email_marketing_consent": {
"state": "subscribed"
},
"sms_marketing_consent": {
"state": "subscribed"
},
"receipt_upload_url": "<string>",
"referral_urls": {
"direct": "<string>",
"facebook": "<string>",
"twitter": "<string>",
"email": "<string>",
"whatsapp": "<string>",
"instagram": "<string>",
"device_share": "<string>"
}
}
}{
"error": {
"message": "<string>",
"details": {}
}
}{
"error": {
"message": "<string>",
"details": {}
}
}{
"error": {
"message": "<string>",
"details": {}
}
}{
"error": {
"code": "not_found",
"message": "<string>"
}
}{
"error": {
"code": "insufficient_points",
"message": "<string>"
}
}{
"error": {
"code": "rate_limited"
}
}Custom
curl --request POST \
--url https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"reward_id": 123,
"customer_merchant_id": "<string>",
"fulfill_immediately": true
}
'import requests
url = "https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem"
payload = {
"reward_id": 123,
"customer_merchant_id": "<string>",
"fulfill_immediately": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({reward_id: 123, customer_merchant_id: '<string>', fulfill_immediately: true})
};
fetch('https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'reward_id' => 123,
'customer_merchant_id' => '<string>',
'fulfill_immediately' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem"
payload := strings.NewReader("{\n \"reward_id\": 123,\n \"customer_merchant_id\": \"<string>\",\n \"fulfill_immediately\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"reward_id\": 123,\n \"customer_merchant_id\": \"<string>\",\n \"fulfill_immediately\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.loyaltylion.com/headless/2025-06/{site_id}/rewards/custom/redeem")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"reward_id\": 123,\n \"customer_merchant_id\": \"<string>\",\n \"fulfill_immediately\": true\n}"
response = http.request(request)
puts response.read_body{
"fulfillment_explanation": "<string>",
"claimed_reward": {
"id": 123,
"claimed_at": "<string>",
"reward_id": 123,
"title": "$5 voucher",
"state": "approved",
"source": {
"kind": "points_redemption",
"points_cost": 123
},
"auto_refund_at": "<string>",
"redeemable": {
"usage_status": "not_used",
"kind": "custom",
"fulfilment_explanation": "<string>",
"fulfilled_at": "<string>",
"code": "<string>"
}
},
"customer": {
"state": "enrolled",
"id": 123,
"merchant_id": "<string>",
"email": "<string>",
"first_name": "<string>",
"last_name": "<string>",
"points_approved": 123,
"points_pending": 123,
"points_spent": 123,
"enrolled_at": "<string>",
"tier_membership": {
"tier_id": 123,
"started_at": "2025-01-01T12:00:00Z",
"expires_at": "2026-01-01T12:00:00Z",
"progress": {
"kind": "points",
"valid_until": "<string>",
"points_needed_for_renewal": 123,
"upgrade_tier_id": 123,
"points_needed_for_upgrade": 123,
"tier_points": {
"now": 123,
"at_expiration": 123
},
"forecasted_tier_id": 123
}
},
"birthday": {
"month": 6,
"day": 25
},
"next_birthday": "2025-06-25",
"claimed_rewards": [
{
"id": 123,
"claimed_at": "<string>",
"reward_id": 123,
"title": "$5 voucher",
"state": "approved",
"source": {
"kind": "points_redemption",
"points_cost": 123
},
"auto_refund_at": "<string>",
"redeemable": {
"usage_status": "not_used",
"kind": "gift_card",
"code_last_characters": "<string>",
"initial_balance": {
"amount": 123,
"formatted": "<string>"
},
"current_balance": {
"amount": 123,
"formatted": "<string>"
},
"expires_at": "<string>"
}
}
],
"available_rewards": [
{
"id": 123,
"limit": {
"count": 123,
"interval": "day"
},
"kind": "gift_card",
"properties": {
"initial_balance": {
"amount": 123,
"formatted": "<string>"
}
},
"variant": {
"tier_id": 123,
"title": "$5 voucher",
"description": "Spend your points on $5 off your next order",
"enabled": true,
"cost": {
"kind": "fixed",
"points": 123
},
"cost_text": "100 points"
},
"context": {
"can_redeem": {
"state": "redeemable"
},
"last_claimed_at": "<string>",
"claim_count": 123,
"claim_limit": {
"state": "no_limit"
}
},
"image_url": "<string>"
}
],
"available_rules": [
{
"id": 123,
"limit": {
"count": 123,
"interval": "day"
},
"kind": "birthday",
"variant": {
"tier_id": 123,
"title": "Make a purchase",
"description": "Earn points every time you shop with us",
"enabled": true,
"result": {
"kind": "points",
"per_currency_unit": 1,
"points": 5
},
"result_short_text": "5 points per $1"
},
"context": {
"last_completed_at": "<string>",
"completion_count": 123,
"completion_limit": {
"state": "no_limit"
}
},
"image_url": "<string>"
}
],
"history": [
{
"kind": "earned_points_from_rule",
"label": "Join our program",
"date": "<string>",
"points": 1,
"points_text": "500 points",
"rule": {
"id": 123,
"kind": "birthday"
},
"points_will_approve_at": "<string>",
"points_will_expire_at": "<string>",
"state": "pending"
}
],
"active_cart_redemptions": [
{
"id": "<string>",
"claimed_reward_id": 123,
"expires_at": "<string>",
"product": {
"id": "<string>",
"handle": "<string>",
"title": "<string>",
"url": "<string>",
"image_url": "<string>",
"variant": {
"id": "<string>",
"title": "<string>",
"inventory_count": 123
}
},
"quantity": 123,
"cart_id": "<string>",
"cart_line": {
"kind": "shopify",
"merchandise_id": "gid://shopify/ProductVariant/1001",
"product_id": "gid://shopify/Product/100",
"quantity": 1,
"attributes": [
{
"key": "__lion_sfp_id",
"value": "19fdf4cb"
}
]
},
"current_cart_state": {
"in_cart": true,
"cart_requirements_met": true,
"cart_requirements_detail": {
"minimum_cart_total": {
"state": "no_requirement"
},
"minimum_line_item_quantity": {
"state": "no_requirement"
}
}
}
}
],
"email_marketing_consent": {
"state": "subscribed"
},
"sms_marketing_consent": {
"state": "subscribed"
},
"receipt_upload_url": "<string>",
"referral_urls": {
"direct": "<string>",
"facebook": "<string>",
"twitter": "<string>",
"email": "<string>",
"whatsapp": "<string>",
"instagram": "<string>",
"device_share": "<string>"
}
}
}{
"error": {
"message": "<string>",
"details": {}
}
}{
"error": {
"message": "<string>",
"details": {}
}
}{
"error": {
"message": "<string>",
"details": {}
}
}{
"error": {
"code": "not_found",
"message": "<string>"
}
}{
"error": {
"code": "insufficient_points",
"message": "<string>"
}
}{
"error": {
"code": "rate_limited"
}
}custom reward, which is fulfilled by the merchant
either manually or via a webhook. LoyaltyLion isn’t involved in the fulfillment
of custom rewards.
If the redemption is successful, i.e. the customer has enough points, the
response may include a fulfillment_explanation property. This is a short
plaintext string provided by the merchant when the custom reward was configured,
and is intended to inform the customer how/when the reward will be fulfilled.
For example, “We’ll email you instructions within the next 2 days”
The fulfillment explanation, if present, should be shown to the customer once
the reward has been redeemed. Alternatively, if you know which reward has just
been redeemed, you could build your own custom UI flow following the redemption.
For example, if you have a custom reward that provides access to a special
product collection, you might want to direct the user to that collection once
the redemption completes.
If the custom reward has been configured with a webhook, we will call that webhook
after the redemption is successful, and mark the reward as fulfilled once the
webhook returns a success response.
For custom rewards that are manually fulfilled, you can pass the
fulfill_immediately property to immediately mark the reward as fulfilled.Authorizations
An API key linked to a Program in LoyaltyLion, with a set of permissions (scopes). API keys can be created manually, or acquired through an OAuth2 flow. The API key should be provided as a Bearer token in the Authorization header
Path Parameters
Your LoyaltyLion Site ID
Query Parameters
The sales channel from which this request is made
web, pos, mobile ISO 3166-1 alpha-2 country code for the customer. Used to filter rewards by country availability. If not provided, the site default is used
Body
Body
The LoyaltyLion ID of the reward to redeem
For rewards using manual fulfillment, passing true here will mark the custom reward as fulfilled immediately. This option is ignored if the custom reward is configured to use a webhook for fulfillment
Requires api_token authentication: fulfilling a reward is a merchant-side action, so requests made with a customer session token are rejected when this option is passed
This is useful if you know you'll be fulfilling the reward straight away, or have already fulfilled it
This option does not apply to rewards with a fulfillment_type of voucher: a voucher custom reward is always fulfilled at claim time, when the pooled code is issued to the customer
You can pass a usage object to indicate the reward has been used with an order. Note that the usage won't be applied in LoyaltyLion until the matching order (with the same merchant_id) has been sent to LoyaltyLion
Requires api_token authentication: marking a reward as used is a merchant-side action, so requests made with a customer session token are rejected when this option is passed
This option does not apply to rewards with a fulfillment_type of voucher and is ignored for them: a voucher custom reward is marked as used automatically when the matching order containing its discount code is sent to LoyaltyLion
Show child attributes
Show child attributes
Response
201
Optional fulfillment explanation as a short, plaintext string. If present, this will explain to the customer how and when the reward will be fulfilled
The new claimed reward. If you may need to refund this custom reward later, you should save the claimed_reward.id and use it with the Refund Reward API
Show child attributes
Show child attributes
The customer object, updated as of the reward redemption, i.e. their claimed_rewards will include the new redemption. This customer will always have state set to enrolled, as only enrolled customers can redeem rewards
Show child attributes
Show child attributes