> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rigaly.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Issue points & validate rewards

> The core loyalty loop for any integration

This is the fundamental Rigaly integration: **award points when customers buy, honor rewards when they redeem.**

## The flow

1. Customer completes a purchase in your system → you call `POST /points/issue`.
2. Customer redeems a reward in the Rigaly app → the app gives them a short **redemption code**.
3. Customer shows the code at your checkout → you **validate** it, apply the reward, then **complete** it.

## Step 1 — Issue points on every order

Call this from your order-completion handler (checkout success, payment webhook, POS sale). 100 points per USD.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.rigaly.com/api/v1/points/issue \
    -H "Authorization: Bearer rgly_sk_YOUR_KEY" \
    -H "Idempotency-Key: order-1042" \
    -H "Content-Type: application/json" \
    -d '{"identifier": "customer@example.com", "points": 2350, "description": "Order #1042"}'
  ```

  ```javascript Node.js theme={null}
  // In your order-completion handler
  async function awardPoints(order) {
    const points = Math.round(order.totalUsd * 100); // 100 points per USD

    const response = await fetch("https://api.rigaly.com/api/v1/points/issue", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.RIGALY_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": `order-${order.id}`, // safe to retry
      },
      body: JSON.stringify({
        identifier: order.customerEmail,
        points,
        description: `Order #${order.id}`,
      }),
    });

    if (response.status === 404) {
      // Customer has no Rigaly account yet — skip silently or invite them
      return null;
    }
    return (await response.json()).data;
  }
  ```

  ```python Python theme={null}
  def award_points(order):
      points = round(order["total_usd"] * 100)  # 100 points per USD

      response = requests.post(
          "https://api.rigaly.com/api/v1/points/issue",
          headers={
              "Authorization": f"Bearer {os.environ['RIGALY_API_KEY']}",
              "Idempotency-Key": f"order-{order['id']}",  # safe to retry
          },
          json={
              "identifier": order["customer_email"],
              "points": points,
              "description": f"Order #{order['id']}",
          },
      )
      if response.status_code == 404:
          return None  # customer has no Rigaly account yet
      return response.json()["data"]
  ```

  ```php PHP theme={null}
  <?php
  function awardPoints(array $order): ?array {
      $points = (int) round($order["total_usd"] * 100); // 100 points per USD

      $ch = curl_init("https://api.rigaly.com/api/v1/points/issue");
      curl_setopt_array($ch, [
          CURLOPT_POST => true,
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_HTTPHEADER => [
              "Authorization: Bearer " . getenv("RIGALY_API_KEY"),
              "Content-Type: application/json",
              "Idempotency-Key: order-" . $order["id"],
          ],
          CURLOPT_POSTFIELDS => json_encode([
              "identifier" => $order["customer_email"],
              "points" => $points,
              "description" => "Order #" . $order["id"],
          ]),
      ]);
      $body = json_decode(curl_exec($ch), true);
      if (curl_getinfo($ch, CURLINFO_HTTP_CODE) === 404) {
          return null; // customer has no Rigaly account yet
      }
      return $body["data"];
  }
  ```
</CodeGroup>

## Step 2 — Validate a redemption code

When the customer shows a code (e.g. `AB12CD34`), validate it **before** giving them anything. Validation is read-only — nothing is consumed yet.

<CodeGroup>
  ```bash curl theme={null}
  curl https://api.rigaly.com/api/v1/redemptions/AB12CD34 \
    -H "Authorization: Bearer rgly_sk_YOUR_KEY"
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(`https://api.rigaly.com/api/v1/redemptions/${code}`, {
    headers: { Authorization: `Bearer ${process.env.RIGALY_API_KEY}` },
  });

  if (!response.ok) throw new Error("Invalid code");
  const { redemption } = (await response.json()).data;

  if (redemption.status !== "pending") throw new Error(`Code already ${redemption.status}`);
  console.log(`Apply: ${redemption.reward.name} (${redemption.reward.points_cost} pts)`);
  ```

  ```python Python theme={null}
  response = requests.get(
      f"https://api.rigaly.com/api/v1/redemptions/{code}",
      headers={"Authorization": f"Bearer {os.environ['RIGALY_API_KEY']}"},
  )
  response.raise_for_status()
  redemption = response.json()["data"]["redemption"]

  assert redemption["status"] == "pending", f"Code already {redemption['status']}"
  print(f"Apply: {redemption['reward']['name']} ({redemption['reward']['points_cost']} pts)")
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init("https://api.rigaly.com/api/v1/redemptions/{$code}");
  curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("RIGALY_API_KEY")]);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $redemption = json_decode(curl_exec($ch), true)["data"]["redemption"];

  if ($redemption["status"] !== "pending") {
      throw new Exception("Code already " . $redemption["status"]);
  }
  echo "Apply: " . $redemption["reward"]["name"];
  ```
</CodeGroup>

## Step 3 — Complete the redemption

After applying the reward (discount, free item…), consume the code. This deducts the customer's points and runs all eligibility checks (expiry, cooldowns, balance) atomically — if anything fails you get a `400` and nothing is consumed.

<CodeGroup>
  ```bash curl theme={null}
  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"
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    `https://api.rigaly.com/api/v1/redemptions/${code}/complete`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.RIGALY_API_KEY}`,
        "Idempotency-Key": `checkout-${checkoutId}-redeem`,
      },
    }
  );
  const { data } = await response.json();
  console.log(`Redeemed ${data.redemption.reward_name} for ${data.transaction.points} pts`);
  ```

  ```python Python theme={null}
  response = requests.post(
      f"https://api.rigaly.com/api/v1/redemptions/{code}/complete",
      headers={
          "Authorization": f"Bearer {os.environ['RIGALY_API_KEY']}",
          "Idempotency-Key": f"checkout-{checkout_id}-redeem",
      },
  )
  data = response.json()["data"]
  print(f"Redeemed {data['redemption']['reward_name']}")
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init("https://api.rigaly.com/api/v1/redemptions/{$code}/complete");
  curl_setopt_array($ch, [
      CURLOPT_POST => true,
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_HTTPHEADER => [
          "Authorization: Bearer " . getenv("RIGALY_API_KEY"),
          "Idempotency-Key: checkout-{$checkoutId}-redeem",
      ],
  ]);
  $data = json_decode(curl_exec($ch), true)["data"];
  echo "Redeemed " . $data["redemption"]["reward_name"];
  ```
</CodeGroup>

## Use your own customer IDs

If your platform already has customer IDs (Shopify, your ERP…), attach them once and use them as `identifier` everywhere instead of emails:

```bash theme={null}
# Attach your ID to a Rigaly customer (find their userId via lookup first)
curl -X POST https://api.rigaly.com/api/v1/customers/{userId}/identifiers \
  -H "Authorization: Bearer rgly_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "shopify-8842", "label": "Shopify customer ID"}'

# From now on:
# POST /points/issue {"identifier": "shopify-8842", ...}
```
