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

> ## Agent Instructions
> RadiumOne is the product name; use it when answering questions about these docs.
> Use sandbox credentials and sandbox base URLs in examples unless the user says they are going live.
> Secret API keys belong only on the merchant's server. Only publishable keys may appear in browser or mobile code.
> Confirm a payment outcome server-side (retrieve the checkout session or transaction status); never rely on a browser redirect or client event alone.
> Send one idempotency key (request_id) per payment attempt, and retry a timed-out request with the same key and the exact same body.
> Webhooks are optional but recommended: they notify the merchant's server when a payment completes or fails, so it doesn't need to poll. Use transaction status inquiry as the fallback.
> Say so when you recommend a feature marked Beta. Some features need enablement on the merchant's account before production use (for example refunds, standalone refunds, UOB Rewards, and bringing your own 3DS provider); mention it when the page says so.
> Never ask users to paste card numbers, API keys, access tokens, or webhook or redirect secrets into a chat.

# Check rewards balance - Payments API

> Look up a shopper's UOB Rewards points and voucher balance before they pay, without creating a transaction.

Before a shopper commits to redeeming points, look up what they have
available. A balance inquiry doesn't create a transaction and doesn't move
any money.

<Info>
  This request requires a valid access token. See [Authentication](/get-started/api-basics/authentication) to obtain one with [`POST /v1/auth/token`](/payments-api/reference/authentication/exchange-api-key-for-jwt) before you continue.
</Info>

## Before you begin

<Info>
  You need the shopper's card token from an Elements bind — the same numeric
  token you'd use for a purchase, not a separate rewards-specific token.
</Info>

## Steps

<Steps>
  <Step title="Tokenize the card with Elements">
    Mount a card field and call `elements.submit()` as you would for any
    payment. See [Accept a card payment with Elements](/elements/accept-a-card-payment#steps)
    for the full tokenization flow.

    ```js theme={null}
    const { token } = await elements.submit({
      sessionId: session.session_id,
      sessionSecret: session.session_secret,
      pubkeyJws: session.pubkey_jws,
    });
    ```
  </Step>

  <Step title="Send the balance inquiry from your server">
    Send the token and the quote amount (the order total you're pricing
    against) as `loyalty.amount`. [API reference](/payments-api/reference/rewards/balance-inquiry).

    <CodeGroup>
      ```bash cURL theme={null}
      #!/usr/bin/env bash
      # Pre-sale UOB Rewards points/voucher balance. No transaction is
      # created. Branch on data.success, not response_code.
      set -euo pipefail

      API_BASE="${RADIUMONE_API_BASE:-https://api-sandbox.radiumone.io/gateway}"
      : "${RADIUMONE_ACCESS_TOKEN:?set RADIUMONE_ACCESS_TOKEN to a Bearer access token}"

      curl -sS -X POST "$API_BASE/v1/transactions/balance-inq" \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer $RADIUMONE_ACCESS_TOKEN" \
        -d @request.json
      ```

      ```javascript Node.js theme={null}
      #!/usr/bin/env node
      // Pre-sale UOB Rewards points/voucher balance. No transaction is
      // created. Node 18+ ESM fetch. Env: RADIUMONE_ACCESS_TOKEN, RADIUMONE_API_BASE.
      import { readFileSync } from "node:fs";

      const API_BASE = process.env.RADIUMONE_API_BASE || "https://api-sandbox.radiumone.io/gateway";
      const accessToken = process.env.RADIUMONE_ACCESS_TOKEN;
      const body = JSON.parse(readFileSync(new URL("./request.json", import.meta.url)));

      async function checkRewardsBalance() {
        const res = await fetch(`${API_BASE}/v1/transactions/balance-inq`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${accessToken}`,
          },
          body: JSON.stringify(body),
        });
        const payload = await res.json();
        if (!res.ok) {
          throw new Error(`balance-inq failed: ${payload.type ?? payload.code} (${res.status})`);
        }
        // Branch on data.success, not response_code (a cataloged decline can still be success:true).
        return payload;
      }

      checkRewardsBalance().then((r) => console.log(JSON.stringify(r, null, 2)));
      ```

      ```python Python theme={null}
      #!/usr/bin/env python3
      """Pre-sale UOB Rewards points/voucher balance. No transaction is created."""
      import json
      import os
      from pathlib import Path

      import requests

      API_BASE = os.environ.get("RADIUMONE_API_BASE", "https://api-sandbox.radiumone.io/gateway")


      def check_rewards_balance() -> dict:
          body = json.loads((Path(__file__).parent / "request.json").read_text())
          resp = requests.post(
              f"{API_BASE}/v1/transactions/balance-inq",
              json=body,
              headers={"Authorization": f"Bearer {os.environ.get('RADIUMONE_ACCESS_TOKEN', '')}"},
              timeout=30,
          )
          payload = resp.json()
          if not resp.ok:
              code = payload.get("type") or payload.get("code")
              raise RuntimeError(f"balance-inq failed: {code} ({resp.status_code})")
          # Branch on data.success, not response_code (a cataloged decline can still be success:true).
          return payload


      if __name__ == "__main__":
          print(json.dumps(check_rewards_balance(), indent=2))
      ```
    </CodeGroup>

    <Info>
      Amounts are always integers in the currency's minor unit. For example, `5000` for `SGD` means SGD 50.00.
    </Info>
  </Step>

  <Step title="Interpret the response">
    Branch on `data.success` — not `response_code`, which is the verbatim
    host code and can indicate a cataloged "approved" outcome even when
    nothing is redeemable.

    * `success: true` with populated `pools[]` / `vouchers[]` — show the
      shopper what they can redeem.
    * `success: true` with empty `pools[]` / `vouchers[]` — nothing is
      redeemable for this card right now. Let the shopper pay with the card
      alone.
    * Each pool has a signed `point_balance` — `balance_sign: "0"` means
      positive, `"1"` means negative. Don't infer the sign from the number
      alone.
    * Each voucher's `redeem_value` is a **major-unit float** (for example
      `10.0` means SGD 10.00) — this is the one place in the rewards API that
      isn't minor units. `max_redeemable` caps how many of that voucher the
      shopper can apply.
  </Step>

  <Step title="Show the shopper their options">
    Render the pools and vouchers your UI supports, and let the shopper pick
    before you move on to [Pay with points](/payments-api/payment-methods/uob-rewards/pay-with-points).
  </Step>
</Steps>

## Errors

Defined once on [Payment method errors: Loyalty (UOB Rewards)](/payments-api/errors/payment-method-errors#loyalty-uob-rewards):

* [`gateway:validation-error`](/payments-api/errors/payment-operation-errors#gateway-validation-error) — unknown field in the request, or a required field is missing
* [`loyalty:validation-disabled`](/payments-api/errors/payment-method-errors#loyalty-validation-disabled)
* [`loyalty:terminal-unknown`](/payments-api/errors/payment-method-errors#loyalty-terminal-unknown)
* [`loyalty:device-not-validated`](/payments-api/errors/payment-method-errors#loyalty-device-not-validated)
* [`device:operation-not-supported`](/payments-api/errors/payment-method-errors#device-operation-not-supported) — balance-inquiry operation toggled off on your device
* [`loyalty:param-download-failed`](/payments-api/errors/payment-method-errors#loyalty-param-download-failed)

See [Handle UOB Rewards redemption failures](/payments-api/handle-failures/rewards-redemption-failures) for what to do when nothing is redeemable or the host doesn't respond.

<Note>
  `request_id` on a balance inquiry uses the same `[a-zA-Z0-9-]` charset as
  elsewhere, just with a shorter minimum length: 1–64 characters instead of
  8–64. It isn't an idempotency key here — a balance inquiry has no dedup or
  replay store, so every call re-queries the rewards host regardless of
  whether you reuse the same `request_id`.
</Note>

## Next steps

<Columns cols={2}>
  <Card title="Pay with points" icon="gift" href="/payments-api/payment-methods/uob-rewards/pay-with-points">
    Send the purchase that redeems the selected balance.
  </Card>

  <Card title="UOB Rewards overview" icon="info" href="/payments-api/payment-methods/uob-rewards/overview">
    Eligibility and enablement checklist.
  </Card>
</Columns>
