Alex Dunmow 182d64403e 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) <noreply@anthropic.com>
2026-06-22 20:51:55 +08:00

14 KiB
Raw Blame History

name description
revenuecat-api-v2 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:

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:

Authorization: Bearer <token>

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:

Content-Type: application/json

Always URL-encode path params such as customer_id.

Use environment filters where relevant:

environment=sandbox
environment=production

Do not infer production state from sandbox data.

Core objects

RevenueCat v2 is project-centred.

Common object hierarchy:

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

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

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

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

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

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

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

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

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

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

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:

{
  "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:

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:

{
  "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:
GET /projects/{project_id}/customers/{customer_id}
  1. Fetch active entitlements:
GET /projects/{project_id}/customers/{customer_id}/active_entitlements
  1. Fetch subscriptions if detail is needed:
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

GET /projects/{project_id}/customers?search=user@example.com

This searches exact $email attribute matches.

Set customer attributes

POST /projects/{project_id}/customers/{customer_id}/attributes
Content-Type: application/json

Use reserved attribute names like $email only according to RevenueCats rules. Keep custom attribute names short and stable.

Grant temporary access

POST /projects/{project_id}/customers/{customer_id}/actions/grant_entitlement

Explain that this creates a promotional subscription.

Revoke granted access

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:

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

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:

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/..."