- Rigaly enforces: code validity, expiry, one-time use, cooldowns, time-of-day windows (
time_active), points balance. - Your code enforces: which products the reward applies to — using the reward’s
conditionstext (or your own mapping keyed by reward ID).
1. Create the reward with machine-readable conditions
Put your product rule inconditions. Customers see this text in the app, and your checkout gets it back on validation — a simple convention like SKU: prefixes makes it parseable:
curl -X POST https://api.rigaly.com/api/v1/rewards \
-H "Authorization: Bearer rgly_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Free Medium Coffee",
"description": "Redeem for any medium hot coffee",
"points_cost": 500,
"conditions": "Valid for medium hot coffees only. SKU:COF-M-*"
}'
For more structure, keep a mapping in your own system keyed by the reward
id (returned at creation) — e.g. { "9970514e-...": { "skuPattern": "^COF-M-" } } — and use conditions purely as customer-facing text.2. At checkout: validate → check cart → complete
async function redeemRewardCode(code, cart, checkoutId) {
const headers = { Authorization: `Bearer ${process.env.RIGALY_API_KEY}` };
// 1. Validate (read-only)
const validation = await fetch(
`https://api.rigaly.com/api/v1/redemptions/${code}`,
{ headers }
);
if (!validation.ok) throw new Error("Invalid or unknown code");
const { redemption } = (await validation.json()).data;
if (redemption.status !== "pending") {
throw new Error(`Code already ${redemption.status}`);
}
// 2. YOUR check: does the cart contain a qualifying product?
// Convention: "SKU:<pattern>" inside the reward's conditions text
const skuRule = redemption.reward.conditions?.match(/SKU:([\w*-]+)/)?.[1];
if (skuRule) {
const pattern = new RegExp("^" + skuRule.replace(/\*/g, ".*") + "$");
const qualifying = cart.items.find((item) => pattern.test(item.sku));
if (!qualifying) {
throw new Error(`"${redemption.reward.name}" requires a qualifying product in the cart`);
}
applyDiscount(cart, qualifying); // your business logic
}
// 3. Complete — deducts points, marks the code used (atomic)
const completion = await fetch(
`https://api.rigaly.com/api/v1/redemptions/${code}/complete`,
{
method: "POST",
headers: { ...headers, "Idempotency-Key": `checkout-${checkoutId}-redeem` },
}
);
if (!completion.ok) {
revertDiscount(cart); // completion failed (cooldown, balance…) — undo
const { error } = await completion.json();
throw new Error(error.message);
}
return (await completion.json()).data;
}
import re
def redeem_reward_code(code, cart, checkout_id):
headers = {"Authorization": f"Bearer {os.environ['RIGALY_API_KEY']}"}
# 1. Validate (read-only)
validation = requests.get(
f"https://api.rigaly.com/api/v1/redemptions/{code}", headers=headers
)
validation.raise_for_status()
redemption = validation.json()["data"]["redemption"]
if redemption["status"] != "pending":
raise ValueError(f"Code already {redemption['status']}")
# 2. YOUR check: does the cart contain a qualifying product?
conditions = redemption["reward"].get("conditions") or ""
sku_rule = re.search(r"SKU:([\w*-]+)", conditions)
if sku_rule:
pattern = re.compile("^" + sku_rule.group(1).replace("*", ".*") + "$")
qualifying = next((i for i in cart["items"] if pattern.match(i["sku"])), None)
if not qualifying:
raise ValueError(f"'{redemption['reward']['name']}' requires a qualifying product")
apply_discount(cart, qualifying) # your business logic
# 3. Complete — deducts points, marks the code used (atomic)
completion = requests.post(
f"https://api.rigaly.com/api/v1/redemptions/{code}/complete",
headers={**headers, "Idempotency-Key": f"checkout-{checkout_id}-redeem"},
)
if not completion.ok:
revert_discount(cart)
raise ValueError(completion.json()["error"]["message"])
return completion.json()["data"]
<?php
function redeemRewardCode(string $code, array &$cart, string $checkoutId): array {
$auth = "Authorization: Bearer " . getenv("RIGALY_API_KEY");
// 1. Validate (read-only)
$ch = curl_init("https://api.rigaly.com/api/v1/redemptions/{$code}");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [$auth]]);
$redemption = json_decode(curl_exec($ch), true)["data"]["redemption"] ?? null;
if (!$redemption || $redemption["status"] !== "pending") {
throw new Exception("Invalid or already-used code");
}
// 2. YOUR check: does the cart contain a qualifying product?
if (preg_match('/SKU:([\w*-]+)/', $redemption["reward"]["conditions"] ?? "", $m)) {
$pattern = '/^' . str_replace('*', '.*', $m[1]) . '$/';
$qualifying = null;
foreach ($cart["items"] as $item) {
if (preg_match($pattern, $item["sku"])) { $qualifying = $item; break; }
}
if (!$qualifying) {
throw new Exception($redemption["reward"]["name"] . " requires a qualifying product");
}
applyDiscount($cart, $qualifying); // your business logic
}
// 3. Complete — deducts points, marks the code used (atomic)
$ch = curl_init("https://api.rigaly.com/api/v1/redemptions/{$code}/complete");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [$auth, "Idempotency-Key: checkout-{$checkoutId}-redeem"],
]);
$body = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) !== 200) {
revertDiscount($cart);
throw new Exception($body["error"]["message"]);
}
return $body["data"];
}
# 1. Validate — returns the reward with its conditions text
curl https://api.rigaly.com/api/v1/redemptions/AB12CD34 \
-H "Authorization: Bearer rgly_sk_YOUR_KEY"
# 2. (your system checks the cart against reward.conditions)
# 3. Complete
curl -X POST https://api.rigaly.com/api/v1/redemptions/AB12CD34/complete \
-H "Authorization: Bearer rgly_sk_YOUR_KEY" \
-H "Idempotency-Key: checkout-981-redeem"