From 182d64403eef134c3ed0750cf11632678bfb6cf8 Mon Sep 17 00:00:00 2001 From: Alex Dunmow Date: Mon, 15 Jun 2026 13:22:57 +0800 Subject: [PATCH] feat: add revenuecat-api-v2 reference skill Server-side reference for the RevenueCat REST API v2: endpoint map, sk_/atk_ auth, pagination, expandables, rate limits, error types, and write-safety. Symlinked into ~/.claude/skills and listed in README. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 1 + revenuecat-api-v2/SKILL.md | 480 +++++++++++++++++++++++++++++++++++++ 2 files changed, 481 insertions(+) create mode 100644 revenuecat-api-v2/SKILL.md diff --git a/README.md b/README.md index bc53fc9..78bfc9f 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Each skill directory (and command file under `commands/`) is symlinked back into - **developing-blockninja-plugins** — creating, building, and publishing BlockNinja CMS plugins/themes - **fleet** — apply a change across many BlockNinja repos with per-repo verification - **grill-with-docs** — stress-test a plan against the domain model and update docs inline +- **revenuecat-api-v2** — server-side reference for the RevenueCat REST API v2 (endpoints, auth, write-safety) - **shipit** — full BlockNinja plugin/theme pre-publish pipeline - **star-response-builder** — turn experience/achievements into STAR-style responses - **workorder** — generate numbered work-order documents (WO-NNN) diff --git a/revenuecat-api-v2/SKILL.md b/revenuecat-api-v2/SKILL.md new file mode 100644 index 0000000..80e0664 --- /dev/null +++ b/revenuecat-api-v2/SKILL.md @@ -0,0 +1,480 @@ +--- +name: revenuecat-api-v2 +description: Use when integrating, inspecting, automating, or debugging the RevenueCat REST API v2 server-side — reading customer subscription/entitlement/access state, creating or updating customers and attributes, granting or revoking promotional access, managing products, entitlements, offerings, packages, paywalls, apps, or webhooks, and handling refunds, cancellations, metrics, audit logs, or receipt/state migrations. Covers the v2 endpoint map, sk_/atk_ auth, pagination, expandables, rate limits, error types, and write-safety. Server-side REST, not the client SDK. +--- + +# RevenueCat API v2 Skill + +Use this skill when integrating, inspecting, automating, or debugging RevenueCat REST API v2. + +## Source of truth + +Use the current official RevenueCat API v2 docs before implementing. API v2 is still under development and does not cover every v1 use case. Use v1 only when v2 lacks the required endpoint. + +Base URL: + +```text +https://api.revenuecat.com/v2 +``` + +## When to use SDK vs REST + +Use the RevenueCat SDK for normal client purchase flows. + +Use REST API v2 for server-side work: +- reading customer subscription/access state +- creating or updating customers and attributes +- granting or revoking promotional access +- managing products, entitlements, offerings, packages, paywalls, apps, webhooks +- refunds, cancellations, subscription management URLs +- metrics, audit logs, invoices, virtual currencies +- migrations where the backend already has receipts or user state + +## Authentication + +Every request uses: + +```http +Authorization: Bearer +``` + +Valid tokens: +- `sk_...` secret API key: server-to-server, project-scoped. +- `atk_...` OAuth access token: third-party tools, developer-scoped. +- Public SDK keys are for SDK/client configuration only. Never use secret keys in an app, website, repo, or frontend. + +API v1 keys do not work with API v2. Create a v2 secret key and assign only the endpoint permissions needed. + +## Required request habits + +For `POST`, `PUT`, and `PATCH`: + +```http +Content-Type: application/json +``` + +Always URL-encode path params such as `customer_id`. + +Use environment filters where relevant: + +```text +environment=sandbox +environment=production +``` + +Do not infer production state from sandbox data. + +## Core objects + +RevenueCat v2 is project-centred. + +Common object hierarchy: + +```text +Project + Apps + Products + Entitlements + Offerings + Packages + Products + Customers + Attributes + Aliases + Active entitlements + Subscriptions + Purchases + Invoices + Virtual currencies + Paywalls + Webhook integrations + Metrics / audit logs +``` + +Use `project_id` for almost every v2 endpoint. + +## Endpoint map + +### Projects and apps + +```text +GET /projects +POST /projects +GET /projects/{project_id}/apps +POST /projects/{project_id}/apps +GET /projects/{project_id}/apps/{app_id} +POST /projects/{project_id}/apps/{app_id} +DEL /projects/{project_id}/apps/{app_id} +GET /projects/{project_id}/apps/{app_id}/api_keys +GET /projects/{project_id}/apps/{app_id}/store_kit_config +``` + +### Customers + +```text +GET /projects/{project_id}/customers +POST /projects/{project_id}/customers +GET /projects/{project_id}/customers/{customer_id} +DEL /projects/{project_id}/customers/{customer_id} + +GET /projects/{project_id}/customers/{customer_id}/active_entitlements +GET /projects/{project_id}/customers/{customer_id}/subscriptions +GET /projects/{project_id}/customers/{customer_id}/purchases +GET /projects/{project_id}/customers/{customer_id}/aliases +GET /projects/{project_id}/customers/{customer_id}/attributes +POST /projects/{project_id}/customers/{customer_id}/attributes +``` + +Customer search currently supports exact email matching via `$email`. + +### Customer actions + +```text +POST /projects/{project_id}/customers/{customer_id}/actions/transfer +POST /projects/{project_id}/customers/{customer_id}/actions/grant_entitlement +POST /projects/{project_id}/customers/{customer_id}/actions/revoke_granted_entitlement +POST /projects/{project_id}/customers/{customer_id}/actions/assign_offering +POST /projects/{project_id}/customers/{customer_id}/actions/restore_purchase_by_order_id +``` + +Granting an entitlement creates a promotional subscription. Revoking it expires that promotional subscription. Treat both as user-impacting writes. + +### Subscriptions + +```text +GET /projects/{project_id}/subscriptions/{subscription_id} +GET /projects/{project_id}/subscriptions +GET /projects/{project_id}/subscriptions/{subscription_id}/transactions +GET /projects/{project_id}/subscriptions/{subscription_id}/entitlements +GET /projects/{project_id}/subscriptions/{subscription_id}/authenticated_management_url + +POST /projects/{project_id}/subscriptions/{subscription_id}/actions/cancel +POST /projects/{project_id}/subscriptions/{subscription_id}/actions/extend +POST /projects/{project_id}/subscriptions/{subscription_id}/actions/refund +POST /projects/{project_id}/subscriptions/{subscription_id}/transactions/{transaction_id}/actions/refund +``` + +Use subscription fields such as `gives_access`, `status`, `auto_renewal_status`, `pending_payment`, `current_period_ends_at`, `store_subscription_identifier`, and `total_revenue_in_usd`. Prefer `gives_access` and active entitlements over hand-rolled expiry logic. + +### Purchases + +```text +GET /projects/{project_id}/purchases/{purchase_id} +GET /projects/{project_id}/purchases/{purchase_id}/entitlements +GET /projects/{project_id}/purchases +POST /projects/{project_id}/purchases/{purchase_id}/actions/refund +``` + +Use purchase search when starting from a store purchase identifier. + +### Product catalog + +```text +GET /projects/{project_id}/entitlements +POST /projects/{project_id}/entitlements +GET /projects/{project_id}/entitlements/{entitlement_id} +POST /projects/{project_id}/entitlements/{entitlement_id} +DEL /projects/{project_id}/entitlements/{entitlement_id} + +POST /projects/{project_id}/entitlements/{entitlement_id}/actions/archive +POST /projects/{project_id}/entitlements/{entitlement_id}/actions/unarchive +POST /projects/{project_id}/entitlements/{entitlement_id}/actions/attach_products +POST /projects/{project_id}/entitlements/{entitlement_id}/actions/detach_products + +GET /projects/{project_id}/products +POST /projects/{project_id}/products +GET /projects/{project_id}/products/{product_id} +POST /projects/{project_id}/products/{product_id} +DEL /projects/{project_id}/products/{product_id} +POST /projects/{project_id}/products/{product_id}/actions/archive +POST /projects/{project_id}/products/{product_id}/actions/unarchive +POST /projects/{project_id}/products/{product_id}/create_in_store +``` + +Product creation does not create Web Billing products. Store push has store-specific requirements. + +### Offerings and packages + +```text +GET /projects/{project_id}/offerings +POST /projects/{project_id}/offerings +GET /projects/{project_id}/offerings/{offering_id} +POST /projects/{project_id}/offerings/{offering_id} +DEL /projects/{project_id}/offerings/{offering_id} + +GET /projects/{project_id}/offerings/{offering_id}/packages +POST /projects/{project_id}/offerings/{offering_id}/packages +GET /projects/{project_id}/offerings/{offering_id}/packages/{package_id} +POST /projects/{project_id}/offerings/{offering_id}/packages/{package_id} +DEL /projects/{project_id}/offerings/{offering_id}/packages/{package_id} + +POST /projects/{project_id}/offerings/{offering_id}/packages/{package_id}/actions/attach_products +POST /projects/{project_id}/offerings/{offering_id}/packages/{package_id}/actions/detach_products +``` + +Deleting an offering also deletes attached packages. + +### Paywalls + +```text +POST /projects/{project_id}/paywalls/media +GET /projects/{project_id}/paywalls +POST /projects/{project_id}/paywalls +GET /projects/{project_id}/paywalls/{paywall_id} +PATCH /projects/{project_id}/paywalls/{paywall_id} +DEL /projects/{project_id}/paywalls/{paywall_id} +``` + +Paywall updates affect drafts. + +### Webhooks + +```text +GET /projects/{project_id}/integrations/webhooks +POST /projects/{project_id}/integrations/webhooks +GET /projects/{project_id}/integrations/webhooks/{webhook_integration_id} +POST /projects/{project_id}/integrations/webhooks/{webhook_integration_id} +DEL /projects/{project_id}/integrations/webhooks/{webhook_integration_id} +``` + +Webhook fields include name, URL, optional authorization header, environment, event types, and optional app scope. + +### Metrics and audit + +```text +GET /projects/{project_id}/metrics/overview +GET /projects/{project_id}/metrics/revenue +GET /projects/{project_id}/metrics/charts/{chart_id} +GET /projects/{project_id}/metrics/charts/{chart_id}/options +GET /projects/{project_id}/audit_logs +GET /projects/{project_id}/collaborators +``` + +Metrics have stricter rate limits than customer reads. + +## Pagination + +List responses use: + +```json +{ + "object": "list", + "items": [], + "next_page": "...", + "url": "..." +} +``` + +Use: +- `limit`, default `20` +- `starting_after` +- `next_page` until absent + +Pagination is forward-only. + +## Expandables + +Use `expand` when an endpoint supports it to fetch related objects in one request. + +Example: + +```text +GET /projects/{project_id}/customers/{customer_id}?expand=attributes +``` + +Only request expansions the API key has permission to read. + +## Rate limits + +Default domains: +- Customer Information: 480 requests/minute +- Charts & Metrics: 25 requests/minute +- Project Configuration: 60 requests/minute +- Virtual Currency transaction creation: 480 requests/minute + +Read these headers: +- `RevenueCat-Rate-Limit-Current-Usage` +- `RevenueCat-Rate-Limit-Current-Limit` +- `Retry-After` on `429` + +Back off on `429`, `423`, retryable `5xx`, and any response with `retryable: true` or `backoff_ms`. + +## Error handling + +RevenueCat uses normal HTTP status classes: +- `2xx`: success +- `4xx`: client/auth/validation/conflict/rate-limit issue +- `5xx`: RevenueCat/upstream issue + +Error bodies include fields like: + +```json +{ + "type": "parameter_error", + "param": "customer_id", + "message": "id is too long", + "retryable": false, + "doc_url": "..." +} +``` + +Common error types: +- `authentication_error`: bad token +- `authorization_error`: token lacks access to project/permission +- `invalid_request`: wrong method or missing JSON content type +- `parameter_error`: invalid path/query/body value +- `rate_limit_error`: wait using `backoff_ms` / `Retry-After` +- `resource_missing`: bad ID or deleted resource +- `resource_already_exists`: duplicate ID +- `resource_locked_error`: concurrent modification +- `store_error`: upstream app store problem +- `unprocessable_entity_error`: valid syntax, impossible operation +- `entity_references_archived_entities`: unarchive referenced objects first + +## Write safety + +Before any write, state: +- method and endpoint +- required permission +- affected object IDs +- side effects +- whether it touches production or sandbox + +Do not perform destructive/user-impacting writes unless explicitly authorized. + +Treat these as high-risk: +- deleting customers, apps, products, offerings, paywalls, webhooks +- granting/revoking entitlements +- transferring purchases/subscriptions +- refunding purchases/subscriptions +- cancelling/extending subscriptions +- changing production product catalog +- changing production webhooks + +For uncertain writes, provide a dry-run request and explain expected result. + +## Common workflows + +### Check whether a customer has access + +1. Fetch the customer: + +```text +GET /projects/{project_id}/customers/{customer_id} +``` + +2. Fetch active entitlements: + +```text +GET /projects/{project_id}/customers/{customer_id}/active_entitlements +``` + +3. Fetch subscriptions if detail is needed: + +```text +GET /projects/{project_id}/customers/{customer_id}/subscriptions?environment=production +``` + +Use `active_entitlements` and subscription `gives_access`. Do not invent access rules from expiry dates alone. + +### Find a customer by email + +```text +GET /projects/{project_id}/customers?search=user@example.com +``` + +This searches exact `$email` attribute matches. + +### Set customer attributes + +```text +POST /projects/{project_id}/customers/{customer_id}/attributes +Content-Type: application/json +``` + +Use reserved attribute names like `$email` only according to RevenueCat’s rules. Keep custom attribute names short and stable. + +### Grant temporary access + +```text +POST /projects/{project_id}/customers/{customer_id}/actions/grant_entitlement +``` + +Explain that this creates a promotional subscription. + +### Revoke granted access + +```text +POST /projects/{project_id}/customers/{customer_id}/actions/revoke_granted_entitlement +``` + +Explain that this expires the promotional subscription. + +### Build a product catalogue view + +Read in this order: +1. apps +2. products +3. entitlements +4. entitlement product attachments +5. offerings +6. packages +7. package product attachments +8. paywalls + +Use `expand` where supported. + +### Configure webhooks + +Create or update: + +```text +POST /projects/{project_id}/integrations/webhooks +POST /projects/{project_id}/integrations/webhooks/{webhook_integration_id} +``` + +Always specify environment intentionally. Use an authorization header for webhook verification. Do not log webhook secrets. + +### Refund or cancel + +Use the endpoint that matches the store and object: +- Web Billing purchase refund +- Web Billing subscription cancel/refund/extend +- Play Store or Galaxy subscription transaction refund +- subscription management URL for customer-managed changes + +Always confirm production environment and affected subscription/purchase ID. + +## Response standards + +When answering RevenueCat API questions: +- identify SDK vs REST +- identify v1 vs v2 +- state endpoint, method, and required token type +- state required permission scope when relevant +- show minimal `curl` or code using env vars +- never expose real tokens +- call out side effects +- explain retry/backoff behaviour +- convert timestamps to readable ISO dates when shown +- distinguish sandbox from production +- mention when v1 is still required + +## Minimal curl pattern + +```bash +curl -sS \ + -H "Authorization: Bearer $REVENUECAT_API_KEY" \ + -H "Content-Type: application/json" \ + "https://api.revenuecat.com/v2/projects/$REVENUECAT_PROJECT_ID/customers/$CUSTOMER_ID" +``` + +For writes: + +```bash +curl -sS -X POST \ + -H "Authorization: Bearer $REVENUECAT_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"key":"value"}' \ + "https://api.revenuecat.com/v2/projects/$REVENUECAT_PROJECT_ID/..." +```