---
name: gentic-loyalty
description: "Give your AI agent your Rivo loyalty data. Sync points events, redemptions, rewards, referrals, and VIP tiers into your Brain with bounded, resumable syncs, then analyze them in natural language or with SQL via query_data — and join them against your subscription, order, and survey data. Free to sync."
license: MIT
metadata:
  author: gentic
  version: "1.0.0"
---

# Gentic Loyalty

Connect any AI agent to your loyalty program. Sync your Rivo data — points events, redemptions, rewards, referrals, and VIP tiers — into your organization's Brain, then ask in natural language: 'which VIP tier actually repurchases?', 'what are we paying out in points versus getting back?', 'how much revenue came from referrals last quarter?'. Bounded, resumable syncs keep the picture current without re-reading everything.

## When to apply

- User wants to analyze their loyalty program — points earned, redeemed, or outstanding.
- User wants to know how VIP tiers differ in repurchase rate, order value, or retention.
- User wants to measure referral performance — how many, and what revenue they drove.
- User wants to understand reward economics: what a reward costs versus what it returns.
- User wants to sync their Rivo loyalty data into their Brain for analysis.
- User wants to join loyalty data against subscriptions, orders, or survey responses.

## Tools

| Tool | Description | Cost |
|------|-------------|------|
| `rivo_connection_status` | Check whether the calling organization has connected Rivo (the rivo.io loyalty app). Returns `{ connected, shop_domain, updated_at }` when connected, or `{ connected: false }` with a hint otherwise. Free. Call before rivo_sync so a missing integration produces actionable guidance rather than a failed sync. Never returns the API key.  Set check_live to true to also VERIFY the stored key against Rivo with a single one-row read. Worth doing once after connecting a new Rivo account: a key that is present is not the same as a key that works, and the cheapest time to learn the difference is before a sync. | Free |
| `rivo_sync` | Sync loyalty data from Rivo (rivo.io) into this organization's data backend — READ-ONLY, verbatim, one BOUNDED chunk per call. Uses the org's connected Rivo integration (dashboard → Integrations; no API key is passed here, and there is no shop argument — a Rivo key is scoped to one store). Warehouses five resources into per-org tables: `rivo_points_events`, `rivo_points_redemptions`, `rivo_rewards`, `rivo_referrals`, `rivo_vip_tiers`, upserted on their id so re-syncs refresh rows in place. Covers redemption trends, earning velocity, reward economics, referral performance and tier structure — and joins against the org's other connector tables (subscriptions, orders, surveys) in the same warehouse. BOUNDED + RESUMABLE: each call pages only until `max_pages_per_run` pages OR `max_seconds_per_run` seconds are reached, then returns `has_more` + `next_cursor: { resource, since_id }`. Call again passing that back as `cursor` until `has_more` is false — the walk resumes exactly where it stopped. Returns `{ synced: { <resource>: count }, has_more, next_cursor, persisted, pages_fetched, stalled }`. A non-empty `stalled` means a resource stopped because its cursor could not advance rather than because it finished — that is not a complete walk, and it needs a look rather than another call. FREE. | Free |

## Workflow

### 1. Connect Rivo once, then sync without credentials

The Loyalty server sources the Rivo connection (the REST API key) from the org's connected integration (Gentic dashboard → Integrations → Rivo). You never pass credentials to the tools — connect once in the dashboard and the server reads the encrypted connection automatically. Call `rivo_connection_status` first (it's free) to confirm the org is connected; if it returns `{ connected: false }`, point the user to Integrations → Rivo. It never returns the API key. Pass `check_live` when you want it to actually probe Rivo rather than just report that a key is stored.

### 2. Sync loyalty data into the Brain with `rivo_sync`

`rivo_sync` is **free**. It is READ-ONLY and warehouses five resources into per-org tables — `rivo_points_events`, `rivo_points_redemptions`, `rivo_rewards`, `rivo_referrals`, `rivo_vip_tiers` — dedupe-upserted on their natural key, so a re-sync refreshes existing rows in place rather than duplicating them. Returns `{ synced: { <resource>: count }, has_more, next_cursor, persisted, pages_fetched, stalled }`.

### 3. Walk the data with bounded, resumable pagination

Each `rivo_sync` call is BOUNDED: it pages the requested resources only until `max_pages_per_run` pages OR `max_seconds_per_run` seconds are reached, then returns `has_more` and a `next_cursor: { resource, since_id }`. To finish a large backfill, call again passing that `next_cursor` back as `cursor` until `has_more` is false — the walk resumes exactly where it stopped, and because it's an id cursor rather than a page offset, rows inserted mid-walk can't shift the window under you. Use `resources` to sync only the tables you need. Check `stalled` before you trust a walk as complete: a non-empty `stalled` means a resource stopped because its cursor could not advance, not because it ran out of rows — that needs a look rather than another call.

### 4. Count and aggregate with `query_data`

The loyalty tools warehouse data; they don't analyze it. For points issued versus redeemed, redemption rate by reward, referral counts and revenue, or tier distribution, use `query_data` (Gentic Data MCP) over the `rivo_*` tables — standard SQL GROUP BY / COUNT / SUM over the synced rows. Sync first (or re-sync incrementally) so the numbers are current.

### 5. Join loyalty against the rest of the business — this is the point

A loyalty dashboard can tell you redemption counts. It cannot tell you whether the program works, because the answer lives in tables it has never seen. Since everything lands in the same per-org Brain, join `rivo_*` against what you've synced elsewhere: VIP tier against subscription retention (`skio_*` / `loop_*`) for LTV by tier, points redemptions against order history for whether earning actually drives repurchase, referrals against your ad-attributed revenue to see what you were already paying for, and tier or redemption behaviour against survey responses (`fairing_*`) for why. Compose it across tables with `query_data`.

### 6. Present results clearly

Don't dump raw JSON. Summarize the numbers — points issued and redeemed, top rewards, referral counts and revenue, tier distribution — and cite the counts. After a sync, report three things, not two: how many rows were `persisted` per resource, whether `has_more` is true (there's more to page through), and whether anything `stalled`. The third is the one that changes what you say. A stalled resource means the walk is INCOMPLETE even when `has_more` is false, so a summary of persisted counts alone would read as a finished sync when it isn't — name the stalled resources and say the data is partial rather than presenting the run as successful. For recurring checks, re-run `rivo_sync` to refresh the tables before querying.

## Notes

- Organization-scoped: the Rivo connection comes from the org's connected integration (dashboard → Integrations → Rivo). No credential is ever passed to the tools.
- Cost: both `rivo_connection_status` and `rivo_sync` are **free**. You pay only for analytics you run over the synced data (e.g. `query_data`). Pricing is pulled live from the Gentic MCP manifest.
- `rivo_sync` is READ-ONLY. It warehouses your loyalty data and never writes back to Rivo — it cannot grant points, issue rewards, or change a customer's tier. That boundary is deliberate: a connector that can award value is a different product with a different risk profile.
- Rows are deduped on their natural key: re-syncing refreshes the same row (mutable fields and all) rather than duplicating it.
- Syncs are bounded and resumable: each call stops at `max_pages_per_run` / `max_seconds_per_run` and returns `next_cursor: { resource, since_id }`; pass it back as `cursor` to continue until `has_more` is false. A non-empty `stalled` in the result means a resource stopped because its cursor could not advance — that is not a finished walk, and calling again will not fix it.
- The five tables — `rivo_points_events`, `rivo_points_redemptions`, `rivo_rewards`, `rivo_referrals`, `rivo_vip_tiers` — are queried with `query_data` for all counts and aggregations.
- A Rivo API key is scoped to a single store, so which store the data belongs to is captured automatically when you connect and there is nothing to select per call.

## Tool details

- `rivo_connection_status` — Check whether the calling organization has connected Rivo (the rivo.io loyalty app). Returns `{ connected, shop_domain, updated_at }` when connected, or `{ connected: false }` with a hint otherwise. Free. Call before rivo_sync so a missing integration produces actionable guidance rather than a failed sync. Never returns the API key.  Set check_live to true to also VERIFY the stored key against Rivo with a single one-row read. Worth doing once after connecting a new Rivo account: a key that is present is not the same as a key that works, and the cheapest time to learn the difference is before a sync.
  - `check_live` (boolean) — When true, make one minimal read against Rivo to verify the stored key actually works. Adds a round-trip; still free.
- `rivo_sync` — Sync loyalty data from Rivo (rivo.io) into this organization's data backend — READ-ONLY, verbatim, one BOUNDED chunk per call. Uses the org's connected Rivo integration (dashboard → Integrations; no API key is passed here, and there is no shop argument — a Rivo key is scoped to one store). Warehouses five resources into per-org tables: `rivo_points_events`, `rivo_points_redemptions`, `rivo_rewards`, `rivo_referrals`, `rivo_vip_tiers`, upserted on their id so re-syncs refresh rows in place. Covers redemption trends, earning velocity, reward economics, referral performance and tier structure — and joins against the org's other connector tables (subscriptions, orders, surveys) in the same warehouse. BOUNDED + RESUMABLE: each call pages only until `max_pages_per_run` pages OR `max_seconds_per_run` seconds are reached, then returns `has_more` + `next_cursor: { resource, since_id }`. Call again passing that back as `cursor` until `has_more` is false — the walk resumes exactly where it stopped. Returns `{ synced: { <resource>: count }, has_more, next_cursor, persisted, pages_fetched, stalled }`. A non-empty `stalled` means a resource stopped because its cursor could not advance rather than because it finished — that is not a complete walk, and it needs a look rather than another call. FREE.
  - `resources` (array of string) — Subset of resources to sync this run, in order. Any of: points_events, points_redemptions, rewards, referrals, vip_tiers. Omit to sync all five.
  - `page_size` (integer) — Rows per Rivo page (1–100, default 100).
  - `max_pages_per_run` (integer) — Cap on pages fetched this call (default 20, hard max 50). Page more via next_cursor, never via a bigger run.
  - `max_seconds_per_run` (integer) — Wall-clock budget in seconds for this call (default 60, hard max 120). The walk stops after the current page once exceeded; resume with next_cursor.
  - `cursor` — Resume point from a prior call's `next_cursor` — { resource, since_id }. Omit or null to start from the first requested resource.

---

_This SKILL.md is generated from the live Gentic MCP manifest. Tool names, descriptions, and pricing are always current. Connect Gentic Loyalty at https://gentic.co/loyalty._
