Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

Notable changes per release. Dates are UTC.

## [v0.6.1](https://github.com/akshitkrnagpal/revcat/releases/tag/v0.6.1) - 2026-05-17

Release hardening for the public launch.

### Fixed

- `revcat doctor` now exits non-zero when a health check fails, so CI and agents can trust it as a gate.
- `revcat subscriptions search` and `revcat purchases search` now follow `next_page` cursors instead of silently returning only the first page.
- `revcat init` now describes its app-selection default accurately: all apps are selected by default.
- Root help and docs now describe the current mode-0600 file-backed auth model instead of the removed keychain backend.

### Added

- GitHub Actions CI for `make verify` and the docs build.
- pnpm 11 build-script approvals so docs install and build non-interactively.
- Hermetic marketing/demo GIFs for paywall publishing, project bootstrap, customer debugging, catalog inspection, and operations checks.

## [v0.6.0](https://github.com/akshitkrnagpal/revcat/releases/tag/v0.6.0) - 2026-05-02

Drops the keyring backend. `~/.revcat/config.json` (mode 0600) is now the only global credential store.
Expand Down
55 changes: 48 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![Docs](https://img.shields.io/badge/docs-revcat.vercel.app-7c8aff)](https://revcat.vercel.app)
[![Go](https://img.shields.io/github/go-mod/go-version/akshitkrnagpal/revcat)](./go.mod)

The RevenueCat CLI. Run your RevenueCat project from the terminal instead of clicking through the dashboard.
The RevenueCat CLI for terminal-first product teams. Debug customers, inspect catalog wiring, ship paywall updates, manage webhooks, and pull metrics without clicking through the dashboard.

![revcat demo](./demo/demo.gif)

Expand All @@ -19,10 +19,51 @@ revcat publish offering pro --paywall ./paywalls/pro.json

## Why

RevenueCat ships a dashboard, a REST API, and an MCP server, but no first-party CLI. revcat covers the v2 REST API surface accessible via OAuth: full CRUD on entitlements, offerings, packages, products, paywalls, webhooks, virtual currencies; per-customer grants/refunds/transfers; metrics + charts; audit-logs.
RevenueCat ships a dashboard, a REST API, and an MCP server, but no first-party CLI. revcat gives you a fast, scriptable surface for the jobs that are slow or repetitive in the dashboard:

- debug "paid but no premium" support tickets from a store transaction id
- inspect offerings, packages, products, entitlements, and paywalls
- ship paywall config changes and set the current offering in one command
- grant, revoke, refund, transfer, and inspect customer state
- check webhooks, audit logs, charts, metrics, and project configuration
- give CI, scripts, and agents JSON output without another flag

Output is a colored table when you're at a terminal and JSON when you're piping into a script - no `--json` ceremony.

## Demos

All demo GIFs are hermetic recordings backed by local fixtures in [`demo/mock-bin`](./demo/mock-bin/). They show real command syntax without touching a live RevenueCat project.

### Ship a paywall update

Push a paywall config and keep the offering current from the terminal.

![revcat paywall publish demo](./demo/demo.gif)

### Bootstrap project context

Bind a repo once so future commands inherit the right project and credentials.

![revcat init demo](./demo/init.gif)

### Debug and fix customer access

Resolve a store transaction id, inspect the customer, and grant goodwill access.

![revcat customer debug demo](./demo/customer-debug.gif)

### Inspect catalog wiring

Check offerings, products, and package membership without opening the dashboard.

![revcat catalog demo](./demo/catalog.gif)

### Monitor operations

Review webhooks, audit logs, and headline metrics while launch traffic is coming in.

![revcat ops demo](./demo/ops.gif)

## Install

```sh
Expand Down Expand Up @@ -183,18 +224,18 @@ Full docs at <https://revcat.vercel.app> - install, quickstart, every command, c

revcat ships four [Agent Skills](./skills/) (open standard) so Claude Code, Cursor, and Codex can compose revcat commands accurately:

- `revcat-getting-started` install, auth, top-level command map
- `revcat-commands` real syntax + examples for every subcommand
- `revcat-troubleshooting` common errors and fixes
- `revcat-storefront-debug` 7-step diagnostic for "the SDK sees 0 packages from my offering"
- `revcat-getting-started` - install, auth, top-level command map
- `revcat-commands` - real syntax + examples for every subcommand
- `revcat-troubleshooting` - common errors and fixes
- `revcat-storefront-debug` - 7-step diagnostic for "the SDK sees 0 packages from my offering"

Install via [skills.sh](https://skills.sh) (auto-detects your agent):

```sh
npx skills add akshitkrnagpal/revcat
```

Or manually see [`skills/README.md`](./skills/README.md) for the Claude Code / Cursor / Codex paths.
Or manually - see [`skills/README.md`](./skills/README.md) for the Claude Code / Cursor / Codex paths.

## License

Expand Down
20 changes: 10 additions & 10 deletions demo/README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
# revcat demos

Three [vhs](https://github.com/charmbracelet/vhs) tapes, each rendering one user-facing flow. `demo.gif` is the README hero; the other two anchor specific guide pages.
Five [vhs](https://github.com/charmbracelet/vhs) tapes, each rendering one user-facing flow. `demo.gif` is the README hero; the other GIFs are marketing/support cuts for specific use cases.

All tapes are hermetic by default: they prepend `./mock-bin` to `PATH`, so they show real revcat command syntax without calling RevenueCat or requiring credentials.

| Tape | Output | What it shows |
| --- | --- | --- |
| `demo.tape` | `demo.gif` | Hero: ship a paywall update + set offering current with one orchestrator command. |
| `init.tape` | `init.gif` | Bootstrap: `revcat init` materializes `revcat.toml` + `.revcat/config.json` so subsequent commands inherit project context. |
| `customer-debug.tape` | `customer-debug.gif` | Support flow: resolve a store transaction id back to a customer, pull their state, surface the entitlements catalog. Read-only; the grant command is shown as a comment. |
| `customer-debug.tape` | `customer-debug.gif` | Support flow: resolve a store transaction id back to a customer, inspect access, then grant goodwill access. |
| `catalog.tape` | `catalog.gif` | Catalog inspection: offerings, products, and package membership. |
| `ops.tape` | `ops.gif` | Ops checks: webhooks, audit logs, and headline metrics. |

## Regenerate

```sh
brew install vhs

# revcat must be on $PATH. Either bind a project (`revcat init` in this
# directory) so revcat picks up project context from ./.revcat/config.json,
# OR set the env hatch for a one-off:
export REVCAT_REFRESH_TOKEN=rtk_...
export REVCAT_PROJECT_ID=proj_...

cd demo
vhs demo.tape # produces demo.gif
vhs init.tape # produces init.gif
vhs customer-debug.tape # produces customer-debug.gif
vhs catalog.tape # produces catalog.gif
vhs ops.tape # produces ops.gif
```

The `customer-debug.tape` ids are placeholders (`1000000123456789`, `app_user_demo`, `premium`). Edit them to match real ids in your test project before recording. The other two tapes work against any project that has an offering called `default` (for the hero) or just any authed credential (for init).
For social video uploads, duplicate any tape and change `Output something.gif` to `Output something.mp4`; VHS will render an MP4 with the same scene.

`paywall.json` is a sample paywall body kept in this folder so the hero tape is self-contained.
`paywall.json` is a sample paywall body kept in this folder so the hero tape is self-contained. `mock-bin/revcat` owns the deterministic fixture output; update it when a tape adds a new command.
Binary file added demo/catalog.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions demo/catalog.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#####################################################################
# revcat catalog demo - inspect RevenueCat catalog relationships
#
# Shows offerings, products, and package membership without opening the
# RevenueCat dashboard.
#
# Regenerate with:
# brew install vhs
# cd demo
# vhs catalog.tape # produces catalog.gif
#
# Hermetic by default: uses ./mock-bin/revcat and never calls RevenueCat.
#####################################################################

Output catalog.gif

Set Shell "zsh"
Set FontSize 17
Set Width 1280
Set Height 600
Set Theme "Catppuccin Mocha"
Set Padding 24
Set TypingSpeed 28ms
Set PlaybackSpeed 1.0

Hide
Type 'export PS1="%F{12}$ %f"' Enter
Type 'export PATH="$PWD/mock-bin:$PATH"' Enter
Ctrl+L
Show

Type "revcat offerings list"
Sleep 200ms
Enter
Sleep 1600ms

Type "revcat products list"
Sleep 200ms
Enter
Sleep 1600ms

Type "revcat packages list --offering default"
Sleep 200ms
Enter
Sleep 1600ms

Sleep 600ms
Binary file added demo/customer-debug.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 10 additions & 13 deletions demo/customer-debug.tape
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@
#
# Regenerate with:
# brew install vhs
# export REVCAT_REFRESH_TOKEN=rtk_...
# export REVCAT_PROJECT_ID=proj_...
# # Edit the placeholders below to match real ids in your TEST project:
# # STORE_TX_ID, APP_USER_ID, ENTITLEMENT_ID
# cd demo
# vhs customer-debug.tape # produces customer-debug.gif
#
# Hermetic by default: uses ./mock-bin/revcat and never calls RevenueCat.
#####################################################################

Output customer-debug.gif
Expand All @@ -25,11 +23,12 @@ Set Width 1280
Set Height 600
Set Theme "Catppuccin Mocha"
Set Padding 24
Set TypingSpeed 35ms
Set TypingSpeed 28ms
Set PlaybackSpeed 1.0

Hide
Type 'export PS1="%F{12}$ %f"' Enter
Type 'export PATH="$PWD/mock-bin:$PATH"' Enter
Ctrl+L
Show

Expand All @@ -39,26 +38,24 @@ Show
Type "revcat subscriptions search 1000000123456789"
Sleep 200ms
Enter
Sleep 2200ms
Sleep 1600ms

# 2. Now you have the customer. Pull their state.
Type "revcat subscribers info app_user_demo"
Sleep 200ms
Enter
Sleep 2500ms
Sleep 1800ms

# 3. Read-only entitlement list - confirms what you'd attach to.
# The demo stops here so it stays mutation-free.
Type "revcat entitlements list"
Sleep 200ms
Enter
Sleep 2200ms
Sleep 1600ms

# When you're ready to actually grant goodwill access, the next step
# is one line - shown as a comment so this gif doesn't mutate state:
Type "# revcat subscribers grant app_user_demo premium -d 7d --confirm"
# 4. Fix the user in one command. This is fixture output, not live state.
Type "revcat subscribers grant app_user_demo premium -d 7d --confirm"
Sleep 200ms
Enter
Sleep 1800ms
Sleep 1600ms

Sleep 600ms
Binary file modified demo/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 12 additions & 14 deletions demo/demo.tape
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,48 @@
# cd demo
# vhs demo.tape # produces demo.gif
#
# Assumes:
# - revcat is on $PATH (brew install akshitkrnagpal/tap/revcat)
# - this directory has been bound to a project (cd demo && revcat init)
# so revcat picks up project context from ./.revcat/config.json,
# OR REVCAT_REFRESH_TOKEN + REVCAT_PROJECT_ID are set in the env
# - the bound project has an offering called "default" (or edit below)
# Hermetic by default:
# - prepends ./mock-bin to PATH so this recording never calls RevenueCat
# - shows real revcat command syntax against deterministic fixture output
#####################################################################

Output demo.gif

Set Shell "zsh"
Set FontSize 18
Set FontSize 17
Set Width 1280
Set Height 480
Set Height 560
Set Theme "Catppuccin Mocha"
Set Padding 24
Set TypingSpeed 35ms
Set TypingSpeed 28ms
Set PlaybackSpeed 1.0

# Custom prompt: simple $, no zsh continuation `>`.
Hide
Type 'export PS1="%F{12}$ %f"' Enter
Type 'export PATH="$PWD/mock-bin:$PATH"' Enter
Ctrl+L
Show

# 1. Frame the input. Show the paywall body that will ship.
Type "cat paywall.json | jq"
# 1. Show a compact summary of the paywall body that will ship.
Type "jq '{title: .config.title, cta: .config.cta, packages: .config.package_ids}' paywall.json"
Sleep 200ms
Enter
Sleep 1500ms
Sleep 1400ms

# 2. Ship it. The orchestrator does both steps - PUT paywall + set
# offering as current - in one shot. --dry-run prints the plan so
# the demo is reproducible regardless of repo state.
Type "revcat publish offering default --paywall ./paywall.json --dry-run --confirm"
Sleep 200ms
Enter
Sleep 2500ms
Sleep 2200ms

# 3. Confirm. Read-only, just shows that the offering is what the SDK
# will see.
Type "revcat offerings view default"
Sleep 200ms
Enter
Sleep 1500ms
Sleep 1600ms

Sleep 600ms
Binary file modified demo/init.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 13 additions & 13 deletions demo/init.tape
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,33 @@
#
# Regenerate with:
# brew install vhs
# export REVCAT_REFRESH_TOKEN=rtk_...
# export REVCAT_PROJECT_ID=proj_...
# cd demo
# vhs init.tape # produces init.gif
#
# Hermetic by default: uses ./mock-bin/revcat and never calls RevenueCat.
#####################################################################

Output init.gif

Set Shell "zsh"
Set FontSize 18
Set FontSize 17
Set Width 1280
Set Height 560
Set Height 600
Set Theme "Catppuccin Mocha"
Set Padding 24
Set TypingSpeed 35ms
Set TypingSpeed 28ms
Set PlaybackSpeed 1.0

# Setup: throwaway sandbox dir, simple prompt, project_id pre-resolved
# from the env so the survey picker doesn't gate the recording.
# Setup: throwaway sandbox dir, simple prompt, mock revcat on PATH.
Hide
Type 'export PS1="%F{12}$ %f"' Enter
Type 'export DEMO_DIR="$PWD"; export PATH="$DEMO_DIR/mock-bin:$PATH"' Enter
Type 'cd $(mktemp -d)' Enter
Ctrl+L
Show

# 1. Empty directory. Same shape every reader's repo starts as.
Type "ls -la"
Type "find . -maxdepth 2 -type f | sort"
Sleep 200ms
Enter
Sleep 800ms
Expand All @@ -45,24 +45,24 @@ Sleep 800ms
Type "revcat init --no-apps"
Sleep 200ms
Enter
Sleep 2500ms
Sleep 2200ms

# 3. Two files now exist. revcat.toml is the committed half.
Type "ls -la"
Type "find . -maxdepth 2 -type f | sort"
Sleep 200ms
Enter
Sleep 1200ms
Sleep 1000ms

Type "cat revcat.toml"
Sleep 200ms
Enter
Sleep 1500ms
Sleep 1200ms

# 4. Project context is now automatic. No --project-id, no env, just
# works from cwd.
Type "revcat auth status"
Sleep 200ms
Enter
Sleep 2000ms
Sleep 1800ms

Sleep 600ms
Loading
Loading