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

# Manage rule-level points on your side

> Snag does not store or expose per-rule point totals. Learn how to track and query rule-level points in your own storage using webhooks and periodic sync.

<head>
  <script type="application/ld+json">
    {JSON.stringify({
            "@context": "https://schema.org",
            "@graph": [
              {
                "@type": "TechArticle",
                "headline": "Manage rule-level points on your side",
                "description": "Snag does not store or expose per-rule point totals. Learn how to track and query rule-level points in your own storage using webhooks and periodic sync.",
                "author": {"@type": "Organization", "name": "Snag Solutions", "url": "https://www.snagsolutions.io/"},
                "publisher": {"@type": "Organization", "name": "Snag Solutions", "url": "https://www.snagsolutions.io/", "logo": {"@type": "ImageObject", "url": "https://assets.snagsolutions.io/public/docs/snag-logo-dark-no-bg.svg"}},
                "mainEntityOfPage": "https://docs.snagsolutions.io/loyalty/manage-rule-level-points"
              },
              {
                "@type": "BreadcrumbList",
                "itemListElement": [
                  {"@type": "ListItem", "position": 1, "name": "Home", "item": "https://docs.snagsolutions.io/welcome"},
                  {"@type": "ListItem", "position": 2, "name": "Loyalty", "item": "https://docs.snagsolutions.io/loyalty/loyalty-overview"},
                  {"@type": "ListItem", "position": 3, "name": "Advanced workflows", "item": "https://docs.snagsolutions.io/loyalty/managing-user-accounts"},
                  {"@type": "ListItem", "position": 4, "name": "Manage rule level points"}
                ]
              }
            ]
          })}
  </script>
</head>

## Overview

Snag maintains an immutable loyalty ledger and accurate per-currency balances for each user. We do not store or expose the total points a user earned from a single loyalty rule ("rule-level points").

<Warning>
  We don't provide any endpoint to fetch "points by rule" for one user or a list
  of users. If you need rule-level totals, you should track and store them in
  your own system.
</Warning>

<Info>
  What we do provide: a complete transaction stream and balance updates. You can
  reliably derive per-rule totals by consuming transaction events and persisting
  aggregates keyed by `(userId, loyaltyRuleId)` on your side.
</Info>

## Recommended approach

At a high level, you should:

* Ingest new transactions in real time via subscriptions → webhooks
* Periodically reconcile using the transactions listing endpoint
* Maintain idempotent, append-only processing in your datastore

<Steps>
  <Step title="Receive new transactions via webhooks">
    Enable a subscription for `LoyaltyTransactionEntry` and deliver events to your webhook endpoint. See [Subscriptions](/stratus/subscriptions) and [Webhooks](/stratus/webhooks).

    <Warning>
      `LoyaltyTransactionEntry` may not always include a `loyaltyRuleId` (e.g., manual adjustments). Skip entries without a rule when calculating rule-level totals.
    </Warning>
  </Step>

  <Step title="Reconcile periodically">
    Run a periodic job to fetch recent entries and backfill anything missed by
    your webhook consumer. See [Get Loyalty Transaction
    Entries](/api-reference/loyalty/get-loyalty-transaction-entries).
  </Step>

  <Step title="Store minimal aggregates on your side">
    * Keep a lightweight aggregate keyed by `(userId, loyaltyRuleId)`
    * Apply credits and debits accordingly
    * Deduplicate using `entry.id` or `idempotencyKey`
  </Step>
</Steps>

## FAQ

<AccordionGroup>
  <Accordion title="Can Snag add an endpoint to return points per rule?">
    No. Snag's source of truth is the ledger and account balances. Aggregations by rule are intentionally left to partner systems so you can shape them to your business logic and retention needs.
  </Accordion>

  <Accordion title="How do I avoid counting the same transaction twice?">
    Use `entry.id` or `idempotencyKey` to deduplicate. Keep a simple record of
    processed entries and make your updates idempotent.
  </Accordion>

  <Accordion title="What about debits or refunds?">
    Entries include a `direction` of `credit` or `debit`. Apply debits as negative
    adjustments to your stored totals.
  </Accordion>

  <Accordion title="What if a webhook is missed?">
    This is why the periodic reconciliation step exists. Use `startingAfter` with the last processed `entry.id` to fetch any missed entries and catch up exactly once.
  </Accordion>
</AccordionGroup>

## Related docs

* [Subscriptions](/stratus/subscriptions)
* [Webhooks](/stratus/webhooks)
* [Get loyalty transaction entries — API reference](/api-reference/loyalty/get-loyalty-transaction-entries)
* [Checking Rule Completion Status](/loyalty/check-rule-completion-status)

<Check>
  You now have a reliable pattern to calculate, store, and query rule-level
  point totals using Snag's transaction stream.
</Check>
