# Generated by scripts/build-openapi.mjs from scripts/openapi/*.mjs. Do not edit by hand.
# RevenueDot API: open-source, self-hostable backend for in-app purchases that works with the RevenueCat SDK.
openapi: 3.1.0
info:
  title: RevenueDot API
  version: 0.1.0
  summary: Open-source, self-hostable backend for in-app purchases that works with the RevenueCat SDK.
  description: |-
    RevenueDot is an open-source (AGPL-3.0), self-hostable backend for in-app purchases and subscriptions that works with the RevenueCat SDK.

    One server answers four APIs: the SDK endpoints the RevenueCat SDKs call (`/v1`, public app keys), REST API v1 (`/v1`, secret keys), REST API v2 (`/v2`, secret keys or a dashboard session) with RevenueDot extensions, and store notification endpoints.

    Paths, fields and error formats follow RevenueCat's public API so existing SDKs, backends and scripts keep working. Operations marked `x-revenuedot-extension` exist only in RevenueDot.

    RevenueDot is not affiliated with RevenueCat, Inc. Docs: https://revenuedot.app/docs
  license:
    name: AGPL-3.0 (server); this document CC BY 4.0
    identifier: AGPL-3.0-only
  contact:
    name: RevenueDot
    url: https://github.com/revenuedot/revenuedot
servers:
  - url: https://api.revenuedot.app
    description: RevenueDot Cloud
  - url: http://localhost:8787
    description: A local RevenueDot (docker compose up)
  - url: https://{host}
    description: Your self-hosted RevenueDot
    variables:
      host:
        default: revenuedot.example.com
tags:
  - name: Server
    description: Health and server info. No API key.
    x-page: sdk-endpoints
  - name: Customer info
    description: The customer's entitlements, subscriptions and one-time purchases, as the SDK decodes them into `CustomerInfo`.
    x-page: sdk-endpoints
  - name: Receipts
    description: Purchases, restores and syncs. RevenueDot verifies them with the store.
    x-page: sdk-endpoints
  - name: Offerings (SDK)
    description: What the paywall shows.
    x-page: sdk-endpoints
  - name: Identity
    description: "`logIn` and aliases."
    x-page: sdk-endpoints
  - name: Attributes
    description: Customer attributes such as `$email`.
    x-page: sdk-endpoints
  - name: SDK support
    description: Endpoints the SDK calls for features RevenueDot answers minimally, so the SDK keeps working.
    x-page: sdk-endpoints
  - name: Web Billing
    description: Web checkout calls from the iOS SDK and purchases-js. RevenueDot takes no web payments, so a checkout answers an error the SDK shows as a failed purchase.
    x-page: sdk-endpoints
  - name: Store notifications
    description: Where App Store Connect and Google Pub/Sub send server notifications.
    x-page: sdk-endpoints
  - name: Response signing
    description: "Trusted Entitlements: the public key responses are signed with."
    x-page: sdk-endpoints
  - name: Customers (v1)
    description: Secret-key customer operations.
    x-page: rest-v1
  - name: Promotional entitlements (v1)
    description: Grant and revoke access without a purchase.
    x-page: rest-v1
  - name: Offering overrides (v1)
    description: Show one customer a different offering.
    x-page: rest-v1
  - name: Store actions (v1)
    description: Refund, revoke, cancel, defer and extend through the store that sold the subscription.
    x-page: rest-v1
  - name: Projects
    description: Projects hold apps, the catalog, customers and webhooks.
    x-page: rest-v2
  - name: Apps
    description: One app per store, each with its public SDK key and store credentials.
    x-page: rest-v2
  - name: Products
    description: Store products.
    x-page: rest-v2
  - name: Entitlements
    description: The access your app checks, unlocked by products.
    x-page: rest-v2
  - name: Offerings
    description: Groups of packages the paywall shows.
    x-page: rest-v2
  - name: Packages
    description: One choice on the paywall, with one product per app.
    x-page: rest-v2
  - name: Customers
    description: Customers, their attributes, entitlements, subscriptions, purchases and events.
    x-page: rest-v2
  - name: Subscriptions
    description: Subscriptions across customers, and store actions on them.
    x-page: rest-v2
  - name: Purchases
    description: One-time purchases across customers.
    x-page: rest-v2
  - name: Metrics
    description: The dashboard overview numbers.
    x-page: rest-v2
  - name: Webhook integrations
    description: Where events are sent.
    x-page: rest-v2
  - name: Collaborators
    description: Dashboard users of the project.
    x-page: rest-v2
  - name: Dashboard auth
    description: Sign-up, sign-in, password reset, email confirmation, invites and account settings for the dashboard. The session cookie also authorizes REST API v2.
    x-page: extensions
  - name: Members and invites
    description: Invite people to a project by email, change their role, remove them. Dashboard session only.
    x-page: extensions
  - name: Project settings
    description: Project name, transfer behaviour and deletion.
    x-page: extensions
  - name: Store setup
    description: Notification URLs, credential checks, setup health and App Store mass extensions.
    x-page: extensions
  - name: API keys
    description: Secret keys for the REST API.
    x-page: extensions
  - name: Webhook deliveries
    description: Delivery log, manual retry and test events.
    x-page: extensions
  - name: Event log
    description: Every recorded event and money movement.
    x-page: extensions
  - name: Test Store
    description: Simulated purchases and lifecycles for development.
    x-page: extensions
  - name: Dashboard data
    description: Series and rows the dashboard shows.
    x-page: extensions
  - name: Migration import
    description: Bulk import from RevenueCat, used by the `revenuedot import` CLI.
    x-page: extensions
  - name: OAuth for MCP clients
    description: OAuth 2.1 with PKCE so MCP clients can connect to one project without copying a key.
    x-page: extensions
  - name: Webhook events
    description: What RevenueDot POSTs to your webhook URL.
    x-page: webhook-events
paths:
  /:
    get:
      operationId: getServerInfo
      tags:
        - Server
      summary: Server name and docs link
      description: Answers a small JSON document. The Docker health check calls it.
      security: []
      responses:
        "200":
          description: Server info.
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                  docs:
                    type: string
              example:
                name: RevenueDot
                docs: https://revenuedot.app/docs
      x-source: app.ts
  /v1/health:
    get:
      operationId: getHealth
      tags:
        - Server
      summary: Health check
      description: Needs no API key. Use it for load balancer and uptime checks.
      security: []
      responses:
        "200":
          description: The server is up.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    const: ok
              example:
                status: ok
      x-source: routes/sdk.ts
  /v1/health/connectivity:
    get:
      operationId: getHealthConnectivity
      tags:
        - Server
      summary: Connectivity probe
      description: Needs no API key. The iOS SDK probes it only with its internal API failover setting, which is off by default.
      security: []
      responses:
        "200":
          description: The server is up.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    const: ok
              example:
                status: ok
      x-source: routes/sdk.ts
  /.well-known/revenuedot-signing-key:
    get:
      operationId: getSigningKey
      tags:
        - Response signing
      summary: Public key for response signatures
      description: |-
        The Ed25519 root public key this server signs SDK responses with (Trusted Entitlements). Pin this key in SDK builds that verify responses.
        The server signs only when `REVENUEDOT_SIGNING_KEY` is set; otherwise this answers 404. See [Trusted Entitlements](../docs/guides/trusted-entitlements.md).
      security: []
      responses:
        "200":
          description: The key.
          content:
            application/json:
              schema:
                type: object
                properties:
                  algorithm:
                    type: string
                    const: Ed25519
                  public_key:
                    type: string
                    description: Base64 of the raw 32-byte public key.
                  encoding:
                    type: string
                    const: base64
                  header:
                    type: string
                    const: X-Signature
                  docs:
                    type: string
              example:
                algorithm: Ed25519
                public_key: ZzwPxGlon0E8ErpDh9QAH0Jh6+E6D6qufvTSetXZY9Y=
                encoding: base64
                header: X-Signature
                docs: https://revenuedot.app/docs
        "404":
          description: Signing is off.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7259
                message: Response signing is not configured on this server. Set REVENUEDOT_SIGNING_KEY.
      x-source: app.ts
  /v1/subscribers/{app_user_id}:
    get:
      operationId: getCustomerInfo
      tags:
        - Customer info
      summary: Get customer info
      description: |-
        What `Purchases.getCustomerInfo()` calls. Creates the customer when the app user id is new (answer 201). With a secret key the answer also has `subscriber_attributes`.
        Entitlements are listed even after they expire; an entitlement is active while `expires_date` is null or in the future.
      security:
        - publicApiKey: []
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - $ref: "#/components/parameters/XNonce"
      responses:
        "200":
          description: Customer info.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "201":
          description: Customer info of a customer created by this call.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "400":
          $ref: "#/components/responses/V1Error400"
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
    delete:
      operationId: deleteSubscriber
      tags:
        - Customers (v1)
      summary: Delete a customer
      description: Deletes the customer with its aliases, attributes, purchases and events. Cannot be undone.
      security:
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      responses:
        "200":
          description: Deleted.
          content:
            application/json:
              schema:
                type: object
                properties:
                  app_user_id:
                    type: string
              example:
                app_user_id: user_1
        "401":
          $ref: "#/components/responses/V1Error401"
        "403":
          $ref: "#/components/responses/V1Error403"
        "404":
          $ref: "#/components/responses/V1Error404"
      x-source: routes/rest-v1.ts
  /v1/receipts:
    post:
      operationId: postReceipt
      tags:
        - Receipts
      summary: Post a purchase or restore
      description: |-
        Every purchase, restore and `syncPurchases()` ends here. RevenueDot verifies the purchase with the store, saves it, records events and answers the updated customer info.

        - **App Store:** `fetch_token` is a StoreKit 2 signed transaction (JWS), a StoreKit 1 app receipt (base64) or an Xcode StoreKit test receipt. With the app's in-app purchase key, Apple's App Store Server API supplies the full history and renewal state.
        - **Google Play:** `fetch_token` is the purchase token. RevenueDot checks it with the Play Developer API and acknowledges it.
        - **Test Store:** `fetch_token` is `test_<purchase time in ms>_<id>`. Any such token is accepted.

        **4xx or 5xx matters.** A 4xx tells the SDK the purchase can never be accepted, so it finishes the transaction. RevenueDot answers 5xx for its own and the store's temporary failures so the SDK keeps the purchase and retries.
        With a secret key, send `X-Platform` so RevenueDot knows which app the receipt belongs to.
      security:
        - publicApiKey: []
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/XPlatform"
        - $ref: "#/components/parameters/XNonce"
        - name: X-Is-Sandbox
          in: header
          schema:
            type: string
          description: "`true` when the SDK knows the purchase is sandbox (used for StoreKit 1 receipts without an environment)."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                app_user_id:
                  type: string
                  description: The customer posting the receipt.
                fetch_token:
                  type: string
                  description: Receipt, signed transaction, purchase token or Test Store token.
                app_transaction:
                  type: string
                  description: StoreKit 2 app transaction JWS (accepted; not required).
                transaction_id:
                  type: string
                  description: Store transaction id.
                product_id:
                  type: string
                  description: Product being bought.
                product_ids:
                  type: array
                  items:
                    type: string
                platform_product_ids:
                  type: array
                  items:
                    type: object
                    properties:
                      product_id:
                        type: string
                      base_plan_id:
                        type: string
                      offer_id:
                        type: string
                price:
                  type: number
                currency:
                  type: string
                store_country:
                  type: string
                normal_duration:
                  type: string
                  description: ISO 8601 period of the product.
                is_restore:
                  type: boolean
                store_user_id:
                  type: string
                presented_offering_identifier:
                  type: string
                  description: Offering the purchase was made from; it appears in webhooks.
                attributes:
                  type: object
                  description: Customer attributes to save with the purchase.
              required:
                - app_user_id
            example:
              app_user_id: user_1
              fetch_token: test_1790800914000_quickstart
              product_id: pro_monthly
              price: 9.99
              currency: USD
              presented_offering_identifier: default
      responses:
        "200":
          description: Updated customer info, plus `purchased_products`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReceiptResponse"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
                purchased_products:
                  pro_monthly:
                    should_consume: false
        "400":
          $ref: "#/components/responses/V1Error400"
        "401":
          $ref: "#/components/responses/V1Error401"
        "500":
          $ref: "#/components/responses/V1Error500"
        "503":
          $ref: "#/components/responses/V1Error503"
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/offerings:
    get:
      operationId: getOfferings
      tags:
        - Offerings (SDK)
      summary: Get offerings
      description: What `Purchases.getOfferings()` calls. Lists active offerings with the packages whose product belongs to the calling app. `current_offering_id` is the customer's override when one is set.
      security:
        - publicApiKey: []
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      responses:
        "200":
          description: Offerings.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Offerings"
              example:
                current_offering_id: default
                offerings:
                  - description: Standard plans
                    identifier: default
                    metadata: null
                    packages:
                      - identifier: $rc_monthly
                        platform_product_identifier: pro_monthly
                      - identifier: $rc_annual
                        platform_product_identifier: pro_annual
                      - identifier: $rc_lifetime
                        platform_product_identifier: pro_lifetime
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/offerings:
    get:
      operationId: getOfferingsWithoutUser
      tags:
        - Offerings (SDK)
      summary: Get offerings without a user
      description: Same answer as the per-user call, without a customer override.
      security:
        - publicApiKey: []
        - secretApiKey: []
      responses:
        "200":
          description: Offerings.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Offerings"
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/subscribers/identify:
    post:
      operationId: identify
      tags:
        - Identity
      summary: Log in (identify)
      description: |-
        What `Purchases.logIn()` calls. When `new_app_user_id` is new and the current id is anonymous with no other ids, the anonymous customer takes the new id (201).
        When `new_app_user_id` exists, an anonymous-only current customer is merged into it (200). See [Customers and app user IDs](../docs/concepts/customers-and-app-user-ids.md).
      security:
        - publicApiKey: []
        - secretApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                app_user_id:
                  type: string
                  description: The current app user id.
                new_app_user_id:
                  type: string
                  description: Your user id.
              required:
                - app_user_id
                - new_app_user_id
            example:
              app_user_id: $RCAnonymousID:abc123
              new_app_user_id: user_2
      responses:
        "200":
          description: The user existed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "201":
          description: The user is new.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "400":
          $ref: "#/components/responses/V1Error400"
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/alias:
    post:
      operationId: alias
      tags:
        - Identity
      summary: Alias two app user ids
      description: Links `new_app_user_id` to the customer with the same merge rules as log in. The Android SDK uses it for Block Store recovery.
      security:
        - publicApiKey: []
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                new_app_user_id:
                  type: string
              required:
                - new_app_user_id
      responses:
        "200":
          description: Accepted.
          content:
            application/json:
              schema:
                type: object
              example: {}
        "400":
          $ref: "#/components/responses/V1Error400"
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/attributes:
    post:
      operationId: postAttributes
      tags:
        - Attributes
      summary: Set customer attributes
      description: "Saves attributes such as `$email`, `$displayName` or your own keys. A null value deletes the attribute. An invalid `$email` is refused with 7263; the other attributes are saved. `collectDeviceIdentifiers()` sends `$ip` and `$deviceVersion` as `\"true\"`: RevenueDot stores the request's IP address and the device and OS from the SDK's headers instead."
      security:
        - publicApiKey: []
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                attributes:
                  type: object
                  additionalProperties:
                    type: object
                    properties:
                      value:
                        type:
                          - string
                          - "null"
                      updated_at_ms:
                        type: integer
              required:
                - attributes
            example:
              attributes:
                $email:
                  value: ana@example.com
                  updated_at_ms: 1790800914000
      responses:
        "200":
          description: Saved.
          content:
            application/json:
              schema:
                type: object
              example: {}
        "400":
          description: Some attributes were not saved.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7263
                message: Some subscriber attributes keys were unable to be saved.
                attribute_errors:
                  - key_name: $email
                    message: Email address is not a valid email.
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/intro_eligibility:
    post:
      operationId: introEligibility
      tags:
        - SDK support
      summary: Intro offer eligibility (StoreKit 1)
      description: Answers `null` (unknown) for every product, so the SDK decides eligibility on the device.
      security:
        - publicApiKey: []
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                product_identifiers:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          description: Eligibility per product.
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: "null"
              example:
                pro_monthly: null
      x-source: routes/sdk.ts
  /v1/offers:
    post:
      operationId: postOfferForSigning
      tags:
        - SDK support
      summary: Sign a promotional offer (iOS)
      description: |-
        What `Purchases.promotionalOffer(forProductDiscount:product:)` calls. RevenueDot signs each offer with the App Store app's In-App Purchase key (`key_id`, `issuer_id` and `private_key` in the app's credentials) the way Apple verifies it: ECDSA P-256 with SHA-256 over the bundle id, key id, product id, offer id, app account token, nonce and timestamp, DER-encoded and base64 ([Apple's format](https://developer.apple.com/documentation/storekit/generating-a-signature-for-promotional-offers)).
        The app account token matches what the SDK puts on the payment: with StoreKit 2 the lowercase app user id when it is a UUID and empty otherwise; with StoreKit 1 the app user id.
        Without an In-App Purchase key the answer is 400 with code 7234, which the SDK reports as `invalidAppleSubscriptionKeyError` for that offer only.
      security:
        - publicApiKey: []
        - secretApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                app_user_id:
                  type: string
                fetch_token:
                  type: string
                  description: The receipt or signed transaction; not needed for signing.
                generate_offers:
                  type: array
                  items:
                    type: object
                    properties:
                      offer_id:
                        type: string
                        description: Promotional offer id from App Store Connect.
                      product_id:
                        type: string
                    required:
                      - offer_id
                      - product_id
              required:
                - app_user_id
                - generate_offers
            example:
              app_user_id: user_1
              fetch_token: …
              generate_offers:
                - offer_id: winback_50
                  product_id: pro_monthly
      responses:
        "200":
          description: One signature per offer.
          content:
            application/json:
              schema:
                type: object
                properties:
                  offers:
                    type: array
                    items:
                      type: object
                      properties:
                        key_id:
                          type: string
                        offer_id:
                          type: string
                        product_id:
                          type: string
                        signature_data:
                          type: object
                          properties:
                            nonce:
                              type: string
                              description: Lowercase UUID.
                            signature:
                              type: string
                              description: Base64 DER ECDSA signature.
                            timestamp:
                              type: integer
                              description: Epoch milliseconds.
              example:
                offers:
                  - key_id: 2X9R4HXF34
                    offer_id: winback_50
                    product_id: pro_monthly
                    signature_data:
                      nonce: 0f3c2a8e-5d7b-4d7e-9a53-3b8f2c1e6d40
                      signature: MEUCIQDD…
                      timestamp: 1790800914034
        "400":
          description: No In-App Purchase key, or no offers.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7234
                message: Promotional offers need the app's App Store In-App Purchase key. Add it in the app's settings.
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/attribution:
    post:
      operationId: postAttribution
      tags:
        - SDK support
      summary: Attribution data (deprecated iOS call)
      description: |-
        What the deprecated `Purchases.addAttributionData` calls. The advertising identifiers in `data` (`rc_idfa`, `rc_idfv`, `rc_gps_adid`, `rc_ip_address`) become `$idfa`, `$idfv`, `$gpsAdId` and `$ip`.
        For Apple Search Ads (`network` 0) with `iad-attribution` true, the iAd fields become `$mediaSource` ("Apple Search Ads"), `$campaign`, `$adGroup`, `$keyword`, `$creative` and the `$appleAds*` ids.
        Attribution is write-once: a campaign attribute the customer already has is kept.
      security:
        - publicApiKey: []
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                network:
                  type: integer
                  description: "The SDK's AttributionNetwork: 0 Apple Search Ads."
                data:
                  type: object
              required:
                - network
                - data
            example:
              network: 0
              data:
                rc_idfv: 4CEE1BEE-3C19-4591-9E34-1AD968D7B609
                Version3.1:
                  iad-attribution: "true"
                  iad-campaign-name: Spring
                  iad-keyword: scanner
      responses:
        "200":
          description: Stored.
          content:
            application/json:
              schema:
                type: object
              example: {}
        "400":
          $ref: "#/components/responses/V1Error400"
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/adservices_attribution:
    post:
      operationId: postAdServicesAttribution
      tags:
        - SDK support
      summary: Apple AdServices token
      description: |-
        What `enableAdServicesAttributionTokenCollection()` sends once per install (the same token can also arrive as `aad_attribution_token` on a receipt).
        After answering, RevenueDot looks the token up with [Apple's attribution API](https://developer.apple.com/documentation/adservices/aaattribution/attributiontoken()), retrying a 404 or 5xx 3 times 5 seconds apart, and stores an attributed install as `$mediaSource` ("Apple Search Ads"), `$campaign`, `$adGroup`, `$keyword`, `$ad`, `$appleAdsCampaignId`, `$appleAdsAdGroupId`, `$appleAdsKeywordId`, `$appleAdsAdId`, `$appleAdsOrgId`, `$appleAdsCountryOrRegion`, `$claimType` and `$conversionType`. Apple returns ids, not names. They show on the customer page and in every webhook's `subscriber_attributes`.
      security:
        - publicApiKey: []
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                aad_attribution_token:
                  type: string
                  description: The token from AAAttribution.attributionToken().
              required:
                - aad_attribution_token
            example:
              aad_attribution_token: wD3Ma…
      responses:
        "200":
          description: Accepted; the lookup runs after the answer.
          content:
            application/json:
              schema:
                type: object
              example: {}
        "400":
          description: No token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7226
                message: aad_attribution_token is required.
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/health_report_availability:
    get:
      operationId: healthReportAvailability
      tags:
        - SDK support
      summary: SDK health report availability
      security: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      responses:
        "200":
          description: No report logs.
          content:
            application/json:
              schema:
                type: object
                properties:
                  report_logs:
                    type: boolean
              example:
                report_logs: false
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/health_report:
    get:
      operationId: healthReport
      tags:
        - SDK support
      summary: SDK health report
      security:
        - publicApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      responses:
        "200":
          description: Always passed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  project_id:
                    type:
                      - string
                      - "null"
                  app_id:
                    type:
                      - string
                      - "null"
                  checks:
                    type: array
                    items:
                      type: object
              example:
                status: passed
                project_id: proj18pzzkao
                app_id: appvnrm0a5h
                checks: []
      x-source: routes/sdk.ts
  /v1/product_entitlement_mapping:
    get:
      operationId: productEntitlementMapping
      tags:
        - SDK support
      summary: Product to entitlement mapping (offline entitlements)
      description: Lets the SDK grant entitlements while the server cannot be reached.
      security:
        - publicApiKey: []
        - secretApiKey: []
      responses:
        "200":
          description: The mapping.
          content:
            application/json:
              schema:
                type: object
                properties:
                  product_entitlement_mapping:
                    type: object
                    additionalProperties:
                      type: object
                      properties:
                        product_identifier:
                          type: string
                        base_plan_id:
                          type: string
                        entitlements:
                          type: array
                          items:
                            type: string
              example:
                product_entitlement_mapping:
                  pro_monthly:
                    product_identifier: pro_monthly
                    entitlements:
                      - pro
      x-source: routes/sdk.ts
  /v1/customercenter/{app_user_id}:
    get:
      operationId: customerCenter
      tags:
        - SDK support
      summary: Customer Center configuration (not built)
      description: "Always 404 with code 7259: the SDK returns an error and the Customer Center screen shows its error state. Customer Center configuration is planned for Tier 2."
      security:
        - publicApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      responses:
        "404":
          description: Not configured.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7259
                message: Customer Center is not configured.
      x-source: routes/sdk.ts
  /v1/customercenter/support/create-ticket:
    post:
      operationId: customerCenterTicket
      tags:
        - SDK support
      summary: Customer Center support ticket (not built)
      security:
        - publicApiKey: []
      responses:
        "200":
          description: Not sent.
          content:
            application/json:
              schema:
                type: object
                properties:
                  sent:
                    type: boolean
              example:
                sent: false
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/virtual_currencies:
    get:
      operationId: virtualCurrencies
      tags:
        - SDK support
      summary: Virtual currency balances (not built)
      security:
        - publicApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      responses:
        "200":
          description: Empty balances.
          content:
            application/json:
              schema:
                type: object
                properties:
                  virtual_currencies:
                    type: object
              example:
                virtual_currencies: {}
      x-source: routes/sdk.ts
  /v1/subscribers/redeem_purchase:
    post:
      operationId: redeemWebPurchase
      tags:
        - SDK support
      summary: Redeem a web purchase (not available)
      description: "What `Purchases.redeemWebPurchase()` calls with the `redemption_token` from a redemption deep link. RevenueDot takes no web payments, so no token is valid: 400 with code 7849, which the SDKs return as the `invalidToken` result."
      security:
        - publicApiKey: []
      responses:
        "400":
          description: Invalid token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7849
                message: "This redemption link is not valid: RevenueDot has no web purchases to redeem."
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/external_purchase_tokens:
    post:
      operationId: postExternalPurchaseToken
      tags:
        - SDK support
      summary: Register an Apple external purchase token (iOS)
      description: Part of Apple's external purchase and link-out flows, before a web checkout. The token is acknowledged with an id, which is all the SDK reads; the web checkout that follows is not available (see `/rcbilling/v1/hosted-checkout`).
      security:
        - publicApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                app_user_id:
                  type: string
                purchase_type:
                  type: string
                  enum:
                    - IN_APP
                    - LINK_OUT
                token:
                  type: string
                  description: Apple's external purchase token, when there is one.
              required:
                - app_user_id
                - purchase_type
      responses:
        "200":
          description: Registered.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  purchase_type:
                    type: string
                  is_sandbox:
                    type: boolean
                  token_source:
                    type: string
                    enum:
                      - APPLE_SDK
                      - RC_GENERATED
              example:
                id: ept3b1f0c9e2d8a4f6b9c7e5d3a1b2c4d6e
                purchase_type: LINK_OUT
                is_sandbox: true
                token_source: APPLE_SDK
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/ads/reward_verifications/{client_transaction_id}:
    get:
      operationId: rewardVerification
      tags:
        - SDK support
      summary: Rewarded ad verification (not available)
      description: What `pollRewardVerification` polls. There is no server-side ad verification, so the answer is always the final `failed`, and the SDK stops after one request.
      security:
        - publicApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - name: client_transaction_id
          in: path
          required: true
          schema:
            type: string
          description: From `generateRewardVerificationToken`.
      responses:
        "200":
          description: Failed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - pending
                      - verified
                      - failed
                  reward:
                    type: "null"
                  failure_reason:
                    type: string
                  message:
                    type: string
              example:
                status: failed
                reward: null
                failure_reason: not_supported
                message: Server-side reward verification is not available on RevenueDot.
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/receipts/amazon/{store_user_id}/{receipt_id}:
    get:
      operationId: amazonReceipt
      tags:
        - SDK support
      summary: Amazon receipt details (not supported)
      description: "The Android SDK asks for it on Amazon subscription purchases. Amazon Appstore purchases are not supported: 400 with code 7662, the same answer as a receipt post for an Amazon app, which leaves the purchase unconsumed."
      security:
        - publicApiKey: []
      parameters:
        - name: store_user_id
          in: path
          required: true
          schema:
            type: string
        - name: receipt_id
          in: path
          required: true
          schema:
            type: string
          description: Not encoded by the SDK; may contain `/`.
      responses:
        "400":
          description: Not supported.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7662
                message: Amazon Appstore purchases are not supported yet.
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/workflows:
    get:
      operationId: paywallWorkflows
      tags:
        - SDK support
      summary: Paywall workflows (web SDK)
      description: What purchases-js `presentPaywall` asks first. There are no workflows, so the SDK uses the offering's own paywall.
      security:
        - publicApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - name: type
          in: query
          schema:
            type: string
          description: "`paywall`."
      responses:
        "200":
          description: No workflows.
          content:
            application/json:
              schema:
                type: object
                properties:
                  workflows:
                    type: array
                    items:
                      type: object
                  ui_config:
                    type: object
              example:
                workflows: []
                ui_config: {}
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/workflows/{workflow_id}:
    get:
      operationId: paywallWorkflow
      tags:
        - SDK support
      summary: One paywall workflow (web SDK)
      description: Never called, because the workflow list is empty.
      security:
        - publicApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - name: workflow_id
          in: path
          required: true
          schema:
            type: string
      responses:
        "401":
          $ref: "#/components/responses/V1Error401"
        "404":
          description: No such workflow.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7259
                message: Workflow not found.
      x-source: routes/sdk.ts
  /v1/subscribers/{app_user_id}/restore/eligibility:
    post:
      operationId: restoreEligibility
      tags:
        - SDK support
      summary: Restore eligibility (StoreKit 2)
      security:
        - publicApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      responses:
        "200":
          description: Always allowed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  is_purchase_allowed_by_restore_behavior:
                    type: boolean
              example:
                is_purchase_allowed_by_restore_behavior: true
      x-source: routes/sdk.ts
  /v1/config/{domain}:
    parameters:
      - name: domain
        in: path
        required: true
        schema:
          type: string
        description: Config domain the SDK asks for (for example `app`).
    get:
      operationId: getRemoteConfig
      tags:
        - SDK support
      summary: Remote config (none yet)
      description: Answers 204 (no config). `getOfferings` waits on this call.
      security:
        - publicApiKey: []
      responses:
        "204":
          description: No config.
      x-source: routes/sdk.ts
    post:
      operationId: postRemoteConfig
      tags:
        - SDK support
      summary: Remote config (none yet)
      security:
        - publicApiKey: []
      responses:
        "204":
          description: No config.
      x-source: routes/sdk.ts
  /v1/events:
    post:
      operationId: postEvents
      tags:
        - SDK support
      summary: SDK paywall and feature events (accepted, not stored)
      description: Accepted so the SDK does not resend them forever.
      security:
        - publicApiKey: []
      responses:
        "200":
          description: Accepted.
          content:
            application/json:
              schema:
                type: object
              example: {}
      x-source: routes/sdk.ts
  /v1/diagnostics:
    post:
      operationId: postDiagnostics
      tags:
        - SDK support
      summary: SDK diagnostics (accepted, not stored)
      security:
        - publicApiKey: []
      responses:
        "200":
          description: Accepted.
          content:
            application/json:
              schema:
                type: object
              example: {}
      x-source: routes/sdk.ts
  /rcbilling/v1/subscribers/{app_user_id}/products:
    get:
      operationId: testStoreProducts
      tags:
        - Offerings (SDK)
      summary: Test Store product details
      description: Product details the SDK needs for Test Store (and web) products, in the web billing products shape. Prices are 0 until the catalog stores Test Store prices.
      security:
        - publicApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - name: id
          in: query
          schema:
            type: array
            items:
              type: string
          style: form
          explode: true
          description: Product ids; repeat the parameter. None lists every product of the app.
      responses:
        "200":
          description: Product details.
          content:
            application/json:
              schema:
                type: object
                properties:
                  product_details:
                    type: array
                    items:
                      type: object
              example:
                product_details:
                  - identifier: pro_monthly
                    product_type: subscription
                    title: Pro monthly
                    description: null
                    current_price:
                      amount: 0
                      amount_micros: 0
                      currency: USD
                    normal_period_duration: P1M
                    default_purchase_option_id: base
                    default_subscription_option_id: base
                    purchase_options:
                      base:
                        id: base
                        price_id: base
                        base:
                          period_duration: P1M
                          cycle_count: 1
                          price:
                            amount: 0
                            amount_micros: 0
                            currency: USD
                        base_price: null
                        trial: null
                        intro_price: null
                    subscription_options:
                      base:
                        id: base
                        price_id: base
                        base:
                          period_duration: P1M
                          cycle_count: 1
                          price:
                            amount: 0
                            amount_micros: 0
                            currency: USD
                        base_price: null
                        trial: null
                        intro_price: null
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /rcbilling/v1/subscribers/{app_user_id}/offering_products:
    get:
      operationId: webOfferingProducts
      tags:
        - Web Billing
      summary: Web offering products
      description: Defined in the iOS SDK with no caller. There are no web offerings.
      security:
        - publicApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      responses:
        "200":
          description: No web offerings.
          content:
            application/json:
              schema:
                type: object
                properties:
                  offerings:
                    type: object
              example:
                offerings: {}
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /rcbilling/v1/hosted-checkout:
    post:
      operationId: hostedCheckout
      tags:
        - Web Billing
      summary: Start a hosted web checkout (not available)
      description: "The iOS SDK's paywall web checkout. RevenueDot takes no payments: 400 with code 7000, and the SDK returns `failed` for the checkout without retrying."
      security:
        - publicApiKey: []
      responses:
        "400":
          description: Not available.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7000
                message: Web checkout is not available on RevenueDot.
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /rcbilling/v1/purchase:
    post:
      operationId: webBillingPurchase
      tags:
        - Web Billing
      summary: Web Billing purchase (not available)
      description: Defined in purchases-js with no caller. 400 with code 7000.
      security:
        - publicApiKey: []
      responses:
        "400":
          description: Not available.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7000
                message: Web checkout is not available on RevenueDot.
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /rcbilling/v1/checkout/prepare:
    post:
      operationId: checkoutPrepare
      tags:
        - Web Billing
      summary: Prepare a Web Billing checkout (not available)
      description: "purchases-js with an `rcb_` key. 400 with code 7000: the purchase fails with an error in the SDK's purchase screen."
      security:
        - publicApiKey: []
      responses:
        "400":
          description: Not available.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7000
                message: Web checkout is not available on RevenueDot.
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /rcbilling/v1/checkout/start:
    post:
      operationId: checkoutStart
      tags:
        - Web Billing
      summary: Start a Web Billing checkout (not available)
      security:
        - publicApiKey: []
      responses:
        "400":
          description: Not available.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7000
                message: Web checkout is not available on RevenueDot.
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /rcbilling/v1/checkout/{operation_session_id}:
    parameters:
      - name: operation_session_id
        in: path
        required: true
        schema:
          type: string
    get:
      operationId: checkoutStatus
      tags:
        - Web Billing
      summary: Web Billing checkout status
      description: "No checkout session exists: 400 with code 7877."
      security:
        - publicApiKey: []
      responses:
        "400":
          description: No such session.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7877
                message: There is no such checkout session.
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
    patch:
      operationId: checkoutRefreshPricing
      tags:
        - Web Billing
      summary: Refresh Web Billing checkout pricing
      security:
        - publicApiKey: []
      responses:
        "400":
          description: No such session.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7877
                message: There is no such checkout session.
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /rcbilling/v1/checkout/{operation_session_id}/complete:
    parameters:
      - name: operation_session_id
        in: path
        required: true
        schema:
          type: string
    post:
      operationId: checkoutComplete
      tags:
        - Web Billing
      summary: Complete a Web Billing checkout
      security:
        - publicApiKey: []
      responses:
        "400":
          description: No such session.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7877
                message: There is no such checkout session.
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /rcbilling/v1/branding:
    get:
      operationId: webBillingBranding
      tags:
        - Web Billing
      summary: Web checkout branding
      description: "purchases-js with an `rcb_` key loads it before a checkout: the app's name and the SDK's default look."
      security:
        - publicApiKey: []
      responses:
        "200":
          description: Branding.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  app_name:
                    type:
                      - string
                      - "null"
                  app_icon:
                    type:
                      - string
                      - "null"
                  app_icon_webp:
                    type:
                      - string
                      - "null"
                  app_wordmark:
                    type:
                      - string
                      - "null"
                  app_wordmark_webp:
                    type:
                      - string
                      - "null"
                  appearance:
                    type: "null"
                  support_email:
                    type:
                      - string
                      - "null"
                  gateway_tax_collection_enabled:
                    type: boolean
                  brand_font_config:
                    type: "null"
              example:
                id: appvnrm0a5h
                app_name: Scanner Web
                app_icon: null
                app_icon_webp: null
                app_wordmark: null
                app_wordmark_webp: null
                appearance: null
                support_email: null
                gateway_tax_collection_enabled: false
                brand_font_config: null
        "401":
          $ref: "#/components/responses/V1Error401"
      x-source: routes/sdk.ts
  /v1/notifications/apple/{app_id}:
    post:
      operationId: appleNotification
      tags:
        - Store notifications
      summary: App Store Server Notifications v2
      description: |-
        Set this URL (shown on the app's page in the dashboard) as the Production and Sandbox Server URL in App Store Connect, with version 2 notifications.
        RevenueDot verifies Apple's signature and the bundle id, stores the raw body, copies it to `notification_forward_url` when set, and applies it.

        - **200:** handled, including notifications about purchases this server has not seen (stored; applied only with `track_new_purchases`).
        - **400:** the payload cannot be verified or belongs to another app. App Store Connect shows it as failed.
        - **404:** no App Store app with this id.
        - **500:** RevenueDot failed; Apple retries.
      security: []
      parameters:
        - $ref: "#/components/parameters/AppId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                signedPayload:
                  type: string
                  description: Apple's signed JWS.
              required:
                - signedPayload
      responses:
        "200":
          description: Handled.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
              example:
                ok: true
        "400":
          description: Unverifiable.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
              example:
                error: "The signed payload is not valid: bad signature."
        "404":
          description: Unknown app.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
        "500":
          description: Failed; Apple retries.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
      x-source: stores/apple/notifications.ts
  /v1/notifications/google/{app_id}:
    post:
      operationId: googleNotification
      tags:
        - Store notifications
      summary: Google Play real-time developer notifications (Pub/Sub push)
      description: |-
        Set this URL as the endpoint of a Pub/Sub **push** subscription on the topic Google Play publishes to.
        When the app's `pubsub_audience` credential is set, the push must carry a Google-signed OIDC token for that audience (and for `pubsub_service_account` when set).
        Each message is stored once (by message id), forwarded when `notification_forward_url` is set, and applied by reading the purchase from the Play Developer API.

        - **200:** handled, a duplicate, ignored (another package, not a developer notification) or an invalid token that can never succeed.
        - **400:** not a Pub/Sub push body. **401:** bad push token. **404:** no Google Play app with this id.
        - **500 or 503:** a temporary failure; Pub/Sub redelivers.
      security:
        - {}
        - googlePubSubOidc: []
      parameters:
        - $ref: "#/components/parameters/AppId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                message:
                  type: object
                  properties:
                    data:
                      type: string
                      description: Base64 JSON developer notification.
                    messageId:
                      type: string
                    publishTime:
                      type: string
                subscription:
                  type: string
              required:
                - message
      responses:
        "200":
          description: Handled.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - processed
                      - ignored
                      - duplicate
                      - invalid_token
              example:
                status: processed
        "400":
          description: Not a push body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
        "401":
          description: Bad push token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
              example:
                code: 7224
                message: The Pub/Sub push token is missing or invalid.
        "404":
          description: Unknown app.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
        "500":
          description: Temporary failure; Pub/Sub retries.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
        "503":
          description: Google's signing keys could not be loaded.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/V1Error"
      x-source: stores/google/notifications.ts
  /v1/subscribers/{app_user_id}/entitlements/{entitlement_identifier}/promotional:
    post:
      operationId: grantPromotional
      tags:
        - Promotional entitlements (v1)
      summary: Grant promotional access
      description: Gives the customer the entitlement until `end_time_ms`, or for a `duration`. Creates the customer when needed. A grant whose end is within 2 hours of an existing promotional grant for the same entitlement is a duplicate and changes nothing.
      security:
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - name: entitlement_identifier
          in: path
          required: true
          schema:
            type: string
          description: Entitlement lookup key, for example `pro`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                end_time_ms:
                  type: integer
                  description: When access ends, epoch milliseconds. Preferred.
                duration:
                  type: string
                  enum:
                    - daily
                    - three_day
                    - weekly
                    - two_week
                    - monthly
                    - two_month
                    - three_month
                    - six_month
                    - yearly
                    - lifetime
                  description: Deprecated alternative to end_time_ms.
                start_time_ms:
                  type: integer
                  description: Start for `duration`. Default now.
            example:
              duration: weekly
      responses:
        "200":
          description: Customer info with the grant (store `promotional`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "400":
          $ref: "#/components/responses/V1Error400"
        "401":
          $ref: "#/components/responses/V1Error401"
        "403":
          $ref: "#/components/responses/V1Error403"
        "404":
          $ref: "#/components/responses/V1Error404"
      x-source: routes/rest-v1.ts
  /v1/subscribers/{app_user_id}/entitlements/{entitlement_identifier}/revoke_promotionals:
    post:
      operationId: revokePromotionals
      tags:
        - Promotional entitlements (v1)
      summary: Revoke promotional access
      description: Ends every active promotional grant of this entitlement now.
      security:
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - name: entitlement_identifier
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Customer info.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "401":
          $ref: "#/components/responses/V1Error401"
        "403":
          $ref: "#/components/responses/V1Error403"
        "404":
          $ref: "#/components/responses/V1Error404"
      x-source: routes/rest-v1.ts
  /v1/subscribers/{app_user_id}/offerings/{offering_identifier}/override:
    post:
      operationId: overrideOffering
      tags:
        - Offering overrides (v1)
      summary: Show a customer another offering
      description: The customer's `current_offering_id` becomes this offering.
      security:
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - name: offering_identifier
          in: path
          required: true
          schema:
            type: string
          description: Offering id (ofrng...) or lookup key.
      responses:
        "200":
          description: Customer info.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "401":
          $ref: "#/components/responses/V1Error401"
        "403":
          $ref: "#/components/responses/V1Error403"
        "404":
          $ref: "#/components/responses/V1Error404"
      x-source: routes/rest-v1.ts
  /v1/subscribers/{app_user_id}/offerings/override:
    delete:
      operationId: removeOfferingOverride
      tags:
        - Offering overrides (v1)
      summary: Remove a customer's offering override
      security:
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
      responses:
        "200":
          description: Customer info.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "401":
          $ref: "#/components/responses/V1Error401"
        "403":
          $ref: "#/components/responses/V1Error403"
        "404":
          $ref: "#/components/responses/V1Error404"
      x-source: routes/rest-v1.ts
  /v1/subscribers/{app_user_id}/subscriptions/{product_identifier}/revoke:
    post:
      operationId: revokeGoogleSubscription
      tags:
        - Store actions (v1)
      summary: Refund and revoke a Google Play subscription
      description: "Google Play only: refunds the latest payment and ends access now. Other stores answer 400 with code 7000."
      security:
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - name: product_identifier
          in: path
          required: true
          schema:
            type: string
          description: Store product id of the subscription.
      responses:
        "200":
          description: Customer info.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "400":
          $ref: "#/components/responses/V1Error400"
        "401":
          $ref: "#/components/responses/V1Error401"
        "403":
          $ref: "#/components/responses/V1Error403"
        "404":
          $ref: "#/components/responses/V1Error404"
        "503":
          $ref: "#/components/responses/V1Error503"
      x-source: routes/rest-v1.ts
  /v1/subscribers/{app_user_id}/subscriptions/{product_identifier}/defer:
    post:
      operationId: deferGoogleSubscription
      tags:
        - Store actions (v1)
      summary: Defer a Google Play renewal
      description: "Google Play only: moves the next renewal date. Send `expiry_time_ms` or `extend_by_days`. Use extend for App Store subscriptions."
      security:
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - name: product_identifier
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                expiry_time_ms:
                  type: integer
                  description: New expiry, epoch milliseconds; later than the current one.
                extend_by_days:
                  type: integer
                  description: Days to add, 1 to 365.
            example:
              extend_by_days: 7
      responses:
        "200":
          description: Customer info.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "400":
          $ref: "#/components/responses/V1Error400"
        "401":
          $ref: "#/components/responses/V1Error401"
        "403":
          $ref: "#/components/responses/V1Error403"
        "404":
          $ref: "#/components/responses/V1Error404"
        "503":
          $ref: "#/components/responses/V1Error503"
      x-source: routes/rest-v1.ts
  /v1/subscribers/{app_user_id}/transactions/{store_transaction_identifier}/refund:
    post:
      operationId: refundGoogleTransaction
      tags:
        - Store actions (v1)
      summary: Refund a Google Play order
      description: "Google Play only: refunds and revokes the order."
      security:
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - name: store_transaction_identifier
          in: path
          required: true
          schema:
            type: string
          description: Google order id.
      responses:
        "200":
          description: Customer info.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "400":
          $ref: "#/components/responses/V1Error400"
        "401":
          $ref: "#/components/responses/V1Error401"
        "403":
          $ref: "#/components/responses/V1Error403"
        "404":
          $ref: "#/components/responses/V1Error404"
        "503":
          $ref: "#/components/responses/V1Error503"
      x-source: routes/rest-v1.ts
  /v1/subscribers/{app_user_id}/subscriptions/{store_transaction_identifier}/cancel:
    post:
      operationId: cancelGoogleSubscription
      tags:
        - Store actions (v1)
      summary: Cancel a Google Play subscription
      description: "Google Play only: turns auto-renew off; access continues to the end of the period."
      security:
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - name: store_transaction_identifier
          in: path
          required: true
          schema:
            type: string
          description: Store transaction id of the subscription.
      responses:
        "200":
          description: Customer info.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "400":
          $ref: "#/components/responses/V1Error400"
        "401":
          $ref: "#/components/responses/V1Error401"
        "403":
          $ref: "#/components/responses/V1Error403"
        "404":
          $ref: "#/components/responses/V1Error404"
        "503":
          $ref: "#/components/responses/V1Error503"
      x-source: routes/rest-v1.ts
  /v1/subscribers/{app_user_id}/subscriptions/{store_transaction_identifier}/extend:
    post:
      operationId: extendAppleSubscription
      tags:
        - Store actions (v1)
      summary: Extend an App Store subscription
      description: "App Store only (needs the app's in-app purchase key): Apple extends the renewal date. Use defer for Google Play."
      security:
        - secretApiKey: []
      parameters:
        - $ref: "#/components/parameters/AppUserId"
        - name: store_transaction_identifier
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                extend_by_days:
                  type: integer
                  description: 1 to 90.
                extend_reason_code:
                  type: integer
                  description: "Apple's reason code: 0 undeclared, 1 customer satisfaction, 2 other, 3 service issue or outage."
            example:
              extend_by_days: 7
              extend_reason_code: 1
      responses:
        "200":
          description: Customer info.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerInfo"
              example:
                request_date: 2026-09-30T20:41:54Z
                request_date_ms: 1790800914034
                subscriber:
                  entitlements:
                    pro:
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      product_identifier: pro_monthly
                      purchase_date: 2026-09-30T20:41:54Z
                  first_seen: 2026-09-30T20:41:54Z
                  last_seen: 2026-09-30T20:41:54Z
                  management_url: null
                  non_subscriptions: {}
                  original_app_user_id: user_1
                  original_application_version: null
                  original_purchase_date: 2026-09-30T20:41:54Z
                  other_purchases: {}
                  subscriptions:
                    pro_monthly:
                      auto_resume_date: null
                      billing_issues_detected_at: null
                      display_name: null
                      expires_date: 2026-10-30T20:41:54Z
                      grace_period_expires_date: null
                      is_sandbox: true
                      management_url: null
                      original_purchase_date: 2026-09-30T20:41:54Z
                      ownership_type: PURCHASED
                      period_type: normal
                      purchase_date: 2026-09-30T20:41:54Z
                      refunded_at: null
                      store: test_store
                      store_transaction_id: test_1790800914000_quickstart
                      unsubscribe_detected_at: null
                      price:
                        amount: 9.99
                        currency: USD
        "400":
          $ref: "#/components/responses/V1Error400"
        "401":
          $ref: "#/components/responses/V1Error401"
        "403":
          $ref: "#/components/responses/V1Error403"
        "404":
          $ref: "#/components/responses/V1Error404"
        "503":
          $ref: "#/components/responses/V1Error503"
      x-source: routes/rest-v1.ts
  /v2/projects:
    get:
      operationId: listProjects
      tags:
        - Projects
      summary: List projects
      description: A secret key sees its own project. A dashboard session sees every project the user is a member of.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Project"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
      x-source: routes/v2/projects.ts
      x-scopes:
        - project_configuration:projects:read
    post:
      operationId: createProject
      tags:
        - Projects
      summary: Create a project
      description: "Needs a dashboard session: a secret key belongs to one project and cannot create another. The caller becomes the project's admin."
      security:
        - dashboardSession: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 100
              required:
                - name
            example:
              name: Scanner
      responses:
        "200":
          description: The project.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Project"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
      x-source: routes/v2/projects.ts
      x-scopes:
        - project_configuration:projects:read_write
  /v2/projects/{project_id}:
    get:
      operationId: getProject
      tags:
        - Project settings
      summary: Get a project with its settings
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: The project.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectSettings"
              example:
                object: project
                id: proj18pzzkao
                name: My app
                created_at: 1790800900675
                icon_url: null
                icon_url_large: null
                transfer_behavior: transfer
                sandbox_transfer_behavior: null
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/setup.ts
      x-scopes:
        - project_configuration:projects:read
      x-revenuedot-extension: true
    post:
      operationId: updateProject
      tags:
        - Project settings
      summary: Update a project's name and transfer behaviour
      description: See [who owns a restored purchase](../docs/concepts/customers-and-app-user-ids.md#who-owns-a-restored-purchase).
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  maxLength: 100
                transfer_behavior:
                  type: string
                  enum:
                    - transfer
                    - transfer_if_no_active
                    - keep
                    - share
                sandbox_transfer_behavior:
                  type:
                    - string
                    - "null"
                  enum:
                    - transfer
                    - transfer_if_no_active
                    - keep
                    - share
                    - null
            example:
              transfer_behavior: transfer_if_no_active
      responses:
        "200":
          description: The project.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectSettings"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/setup.ts
      x-scopes:
        - project_configuration:projects:read_write
      x-revenuedot-extension: true
    delete:
      operationId: deleteProject
      tags:
        - Project settings
      summary: Delete a project and everything in it
      description: Only a project admin signed in to the dashboard can do this. Apps, catalog, customers, purchases, events and webhooks are deleted. Cannot be undone.
      security:
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: Deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deleted"
              example:
                object: project
                id: …
                deleted_at: 1790801342625
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/setup.ts
      x-scopes:
        - project_configuration:projects:read_write
      x-revenuedot-extension: true
  /v2/projects/{project_id}/collaborators:
    get:
      operationId: listCollaborators
      tags:
        - Collaborators
      summary: List collaborators
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Collaborator"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/setup.ts
      x-scopes:
        - project_configuration:collaborators:read
  /v2/projects/{project_id}/apps:
    get:
      operationId: listApps
      tags:
        - Apps
      summary: List apps
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/App"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/apps.ts
      x-scopes:
        - project_configuration:apps:read
    post:
      operationId: createApp
      tags:
        - Apps
      summary: Create an app
      description: |-
        One app per store. `app_store` and `mac_app_store` need `bundle_id`; `play_store` and `amazon` need `package_name`. The app gets a public SDK key with the store's prefix.
        Other fields in the store object are saved as store credentials (for example `subscription_private_key`, `subscription_key_id`, `subscription_key_issuer`, `play_service_account_credentials_json`). They are never returned.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  maxLength: 255
                type:
                  type: string
                  enum:
                    - amazon
                    - app_store
                    - mac_app_store
                    - play_store
                    - stripe
                    - rc_billing
                    - roku
                    - paddle
                    - test_store
                app_store:
                  type: object
                  description: "`bundle_id` plus optional credentials."
                mac_app_store:
                  type: object
                play_store:
                  type: object
                  description: "`package_name` plus optional credentials."
                amazon:
                  type: object
                stripe:
                  type: object
                rc_billing:
                  type:
                    - object
                    - "null"
                roku:
                  type:
                    - object
                    - "null"
                paddle:
                  type:
                    - object
                    - "null"
              required:
                - name
                - type
            example:
              name: Scanner (iOS)
              type: app_store
              app_store:
                bundle_id: com.example.scanner
      responses:
        "201":
          description: The app.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/App"
              example:
                object: app
                id: appugfw01uy
                name: Scanner (iOS)
                created_at: 1790801342594
                type: app_store
                project_id: proj18pzzkao
                custom_url_scheme: rc-4d13549313
                app_store:
                  bundle_id: com.example.scanner
                  app_store_connect_api_key_configured: false
                  subscription_key_configured: false
                  app_store_connect_vendor_number: null
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/apps.ts
      x-scopes:
        - project_configuration:apps:read_write
  /v2/projects/{project_id}/apps/{app_id}:
    get:
      operationId: getApp
      tags:
        - Apps
      summary: Get an app
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/AppId"
      responses:
        "200":
          description: The app.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/App"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/apps.ts
      x-scopes:
        - project_configuration:apps:read
    post:
      operationId: updateApp
      tags:
        - Apps
      summary: Update an app and its store credentials
      description: |-
        Send only the store object of the app's own type. A field set to null removes that credential; other values replace it.
        RevenueDot extensions in the store object: `notification_forward_url` (copy store notifications to another URL, for example RevenueCat during a dual run; null or "" turns it off), `track_new_purchases`, `allow_unsigned_receipts`, `xcode_certificate`, `app_apple_id`, `pubsub_audience`, `pubsub_service_account`. See [App Store setup](../docs/guides/app-store.md) and [Google Play setup](../docs/guides/google-play.md).
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/AppId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                app_store:
                  type: object
                mac_app_store:
                  type: object
                play_store:
                  type: object
                amazon:
                  type: object
                stripe:
                  type: object
                rc_billing:
                  type: object
                roku:
                  type: object
                paddle:
                  type: object
            example:
              app_store:
                subscription_private_key: |-
                  -----BEGIN PRIVATE KEY-----
                  …
                  -----END PRIVATE KEY-----
                subscription_key_id: ABC123DEFG
                subscription_key_issuer: 57246542-96fe-1a63-e053-0824d011072a
      responses:
        "200":
          description: The app.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/App"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/apps.ts
      x-scopes:
        - project_configuration:apps:read_write
    delete:
      operationId: deleteApp
      tags:
        - Apps
      summary: Delete an app
      description: Deletes the app and its products. Purchase history stays.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/AppId"
      responses:
        "200":
          description: Deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deleted"
              example:
                object: app
                id: …
                deleted_at: 1790801342625
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/apps.ts
      x-scopes:
        - project_configuration:apps:read_write
  /v2/projects/{project_id}/apps/{app_id}/public_api_keys:
    get:
      operationId: listPublicApiKeys
      tags:
        - Apps
      summary: Get an app's public SDK key
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/AppId"
      responses:
        "200":
          description: One key.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/PublicApiKey"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
              example:
                object: list
                items:
                  - object: public_api_key
                    id: pk_appvnrm0a5h
                    key: test_ea5120a23e7b9626f8eff225a627762c
                    environment: sandbox
                    app_id: appvnrm0a5h
                    created_at: 1790800900758
                next_page: null
                url: /v2/projects/proj18pzzkao/apps/appvnrm0a5h/public_api_keys
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/apps.ts
      x-scopes:
        - project_configuration:apps:read
  /v2/projects/{project_id}/products:
    get:
      operationId: listProducts
      tags:
        - Products
      summary: List products
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: app_id
          in: query
          schema:
            type: string
          description: Only this app's products.
        - name: expand
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - items.app
                - items.indicative_price
          style: form
          explode: true
          description: "`items.app` embeds each product's app. `items.indicative_price` adds each product's Test Store price."
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Product"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/products.ts
      x-scopes:
        - project_configuration:products:read
    post:
      operationId: createProduct
      tags:
        - Products
      summary: Create a product
      description: "`store_identifier` is the store's product id. For Google Play subscriptions use `subscriptionId:basePlanId`. Set `subscription.duration` (ISO 8601, for example P1M): the Test Store uses it as the period, and MRR uses it for every store. `test_store_price` sets what the SDK shows for a Test Store product."
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: expand
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - indicative_price
          style: form
          explode: true
          description: "`indicative_price` adds the Test Store price in RevenueCat's IndicativePrice shape (null for other stores and for products without a price)."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                store_identifier:
                  type: string
                  maxLength: 255
                app_id:
                  type: string
                type:
                  type: string
                  enum:
                    - subscription
                    - one_time
                    - consumable
                    - non_consumable
                    - non_renewing_subscription
                display_name:
                  type:
                    - string
                    - "null"
                title:
                  type:
                    - string
                    - "null"
                  description: Alias of display_name.
                price_identifier:
                  type:
                    - string
                    - "null"
                  description: Accepted and ignored.
                subscription:
                  type:
                    - object
                    - "null"
                  properties:
                    duration:
                      type:
                        - string
                        - "null"
                      description: ISO 8601 period such as P1W, P1M, P1Y or P3D.
                test_store_price:
                  type:
                    - object
                    - "null"
                  required:
                    - amount_micros
                    - currency
                  properties:
                    amount_micros:
                      type: integer
                      description: "Price in micros: 9.99 is 9990000."
                    currency:
                      type: string
                      description: ISO 4217 code such as USD or EUR.
                  description: RevenueDot extension. The Test Store price the SDK shows for this product (Test Store products only). Null clears it. Read it back with `expand=indicative_price`.
              required:
                - store_identifier
                - app_id
                - type
            example:
              store_identifier: pro_monthly
              app_id: appvnrm0a5h
              type: subscription
              display_name: Pro monthly
              subscription:
                duration: P1M
      responses:
        "201":
          description: The product.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Product"
              example:
                object: product
                id: prode0zhpfisko
                store_identifier: pro_monthly
                type: subscription
                state: active
                subscription:
                  duration: P1M
                  grace_period_duration: null
                  trial_duration: null
                created_at: 1790800900948
                app_id: appvnrm0a5h
                display_name: Pro monthly
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "409":
          $ref: "#/components/responses/V2Error409"
      x-source: routes/v2/products.ts
      x-scopes:
        - project_configuration:products:read_write
  /v2/projects/{project_id}/products/{product_id}:
    get:
      operationId: getProduct
      tags:
        - Products
      summary: Get a product
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: product_id
          in: path
          required: true
          schema:
            type: string
          description: Product id (prod...).
        - name: expand
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - app
                - indicative_price
          style: form
          explode: true
          description: "`app` embeds the app. `indicative_price` adds the Test Store price in RevenueCat's IndicativePrice shape (null for other stores and for products without a price)."
      responses:
        "200":
          description: The product.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Product"
              example:
                object: product
                id: prode0zhpfisko
                store_identifier: pro_monthly
                type: subscription
                state: active
                subscription:
                  duration: P1M
                  grace_period_duration: null
                  trial_duration: null
                created_at: 1790800900948
                app_id: appvnrm0a5h
                display_name: Pro monthly
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/products.ts
      x-scopes:
        - project_configuration:products:read
    post:
      operationId: updateProduct
      tags:
        - Products
      summary: Update a product
      description: RevenueDot also lets you correct `type` and `subscription.duration` (null clears it), and set or clear `test_store_price`.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: product_id
          in: path
          required: true
          schema:
            type: string
          description: Product id.
        - name: expand
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - app
                - indicative_price
          style: form
          explode: true
          description: "`indicative_price` adds the Test Store price in RevenueCat's IndicativePrice shape (null for other stores and for products without a price)."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                display_name:
                  type: string
                type:
                  type: string
                  enum:
                    - subscription
                    - one_time
                    - consumable
                    - non_consumable
                    - non_renewing_subscription
                subscription:
                  type: object
                  properties:
                    duration:
                      type:
                        - string
                        - "null"
                test_store_price:
                  type:
                    - object
                    - "null"
                  required:
                    - amount_micros
                    - currency
                  properties:
                    amount_micros:
                      type: integer
                      description: "Price in micros: 9.99 is 9990000."
                    currency:
                      type: string
                      description: ISO 4217 code such as USD or EUR.
                  description: RevenueDot extension. The Test Store price the SDK shows for this product (Test Store products only). Null clears it. Read it back with `expand=indicative_price`.
            example:
              display_name: Pro (monthly)
              test_store_price:
                amount_micros: 9990000
                currency: USD
      responses:
        "200":
          description: The product.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Product"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/products.ts
      x-scopes:
        - project_configuration:products:read_write
    delete:
      operationId: deleteProduct
      tags:
        - Products
      summary: Delete a product
      description: Detaches it from entitlements and packages. Purchase history keeps the store id.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: product_id
          in: path
          required: true
          schema:
            type: string
          description: Product id.
      responses:
        "200":
          description: Deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deleted"
              example:
                object: product
                id: …
                deleted_at: 1790801342625
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/products.ts
      x-scopes:
        - project_configuration:products:read_write
  /v2/projects/{project_id}/products/{product_id}/actions/archive:
    post:
      operationId: archiveProduct
      tags:
        - Products
      summary: Archive a product
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: product_id
          in: path
          required: true
          schema:
            type: string
          description: Product id.
      responses:
        "200":
          description: The archived product.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Product"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/products.ts
      x-scopes:
        - project_configuration:products:read_write
  /v2/projects/{project_id}/products/{product_id}/actions/unarchive:
    post:
      operationId: unarchiveProduct
      tags:
        - Products
      summary: Unarchive a product
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: product_id
          in: path
          required: true
          schema:
            type: string
          description: Product id.
      responses:
        "200":
          description: The product.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Product"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/products.ts
      x-scopes:
        - project_configuration:products:read_write
  /v2/projects/{project_id}/entitlements:
    get:
      operationId: listEntitlements
      tags:
        - Entitlements
      summary: List entitlements
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: expand
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - items.product
          style: form
          explode: true
          description: "`items.product` embeds the attached products."
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Entitlement"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/entitlements.ts
      x-scopes:
        - project_configuration:entitlements:read
    post:
      operationId: createEntitlement
      tags:
        - Entitlements
      summary: Create an entitlement
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                lookup_key:
                  type: string
                  description: What apps check, for example pro.
                  maxLength: 200
                display_name:
                  type: string
                  maxLength: 1500
              required:
                - lookup_key
                - display_name
            example:
              lookup_key: pro
              display_name: Pro access
      responses:
        "201":
          description: The entitlement.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Entitlement"
              example:
                object: entitlement
                id: entl1v0bp6r0qs
                project_id: proj18pzzkao
                lookup_key: pro
                display_name: Pro access
                created_at: 1790800901115
                state: active
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "409":
          $ref: "#/components/responses/V2Error409"
      x-source: routes/v2/entitlements.ts
      x-scopes:
        - project_configuration:entitlements:read_write
  /v2/projects/{project_id}/entitlements/{entitlement_id}:
    get:
      operationId: getEntitlement
      tags:
        - Entitlements
      summary: Get an entitlement
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: entitlement_id
          in: path
          required: true
          schema:
            type: string
          description: Entitlement id (entl...).
        - name: expand
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - product
          style: form
          explode: true
          description: "`product` embeds the attached products."
      responses:
        "200":
          description: The entitlement.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Entitlement"
              example:
                object: entitlement
                id: entl1v0bp6r0qs
                project_id: proj18pzzkao
                lookup_key: pro
                display_name: Pro access
                created_at: 1790800901115
                state: active
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/entitlements.ts
      x-scopes:
        - project_configuration:entitlements:read
    post:
      operationId: updateEntitlement
      tags:
        - Entitlements
      summary: Rename an entitlement
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: entitlement_id
          in: path
          required: true
          schema:
            type: string
          description: Entitlement id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                display_name:
                  type: string
              required:
                - display_name
      responses:
        "200":
          description: The entitlement.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Entitlement"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/entitlements.ts
      x-scopes:
        - project_configuration:entitlements:read_write
    delete:
      operationId: deleteEntitlement
      tags:
        - Entitlements
      summary: Delete an entitlement
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: entitlement_id
          in: path
          required: true
          schema:
            type: string
          description: Entitlement id.
      responses:
        "200":
          description: Deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deleted"
              example:
                object: entitlement
                id: …
                deleted_at: 1790801342625
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/entitlements.ts
      x-scopes:
        - project_configuration:entitlements:read_write
  /v2/projects/{project_id}/entitlements/{entitlement_id}/actions/archive:
    post:
      operationId: archiveEntitlement
      tags:
        - Entitlements
      summary: Archive an entitlement
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: entitlement_id
          in: path
          required: true
          schema:
            type: string
          description: Entitlement id.
      responses:
        "200":
          description: The archived entitlement.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Entitlement"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/entitlements.ts
      x-scopes:
        - project_configuration:entitlements:read_write
  /v2/projects/{project_id}/entitlements/{entitlement_id}/actions/unarchive:
    post:
      operationId: unarchiveEntitlement
      tags:
        - Entitlements
      summary: Unarchive an entitlement
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: entitlement_id
          in: path
          required: true
          schema:
            type: string
          description: Entitlement id.
      responses:
        "200":
          description: The entitlement.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Entitlement"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/entitlements.ts
      x-scopes:
        - project_configuration:entitlements:read_write
  /v2/projects/{project_id}/entitlements/{entitlement_id}/products:
    get:
      operationId: listEntitlementProducts
      tags:
        - Entitlements
      summary: List an entitlement's products
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: entitlement_id
          in: path
          required: true
          schema:
            type: string
          description: Entitlement id.
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Product"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/entitlements.ts
      x-scopes:
        - project_configuration:entitlements:read
  /v2/projects/{project_id}/entitlements/{entitlement_id}/actions/attach_products:
    post:
      operationId: attachEntitlementProducts
      tags:
        - Entitlements
      summary: Attach products to an entitlement
      description: Any of these products unlocks the entitlement. Every id must belong to the project, or nothing changes.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: entitlement_id
          in: path
          required: true
          schema:
            type: string
          description: Entitlement id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                product_ids:
                  type: array
                  items:
                    type: string
                  minItems: 1
                  maxItems: 50
              required:
                - product_ids
            example:
              product_ids:
                - prode0zhpfisko
                - prodz2c0dt6z9x
      responses:
        "200":
          description: The entitlement with its products.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Entitlement"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/entitlements.ts
      x-scopes:
        - project_configuration:entitlements:read_write
  /v2/projects/{project_id}/entitlements/{entitlement_id}/actions/detach_products:
    post:
      operationId: detachEntitlementProducts
      tags:
        - Entitlements
      summary: Detach products from an entitlement
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: entitlement_id
          in: path
          required: true
          schema:
            type: string
          description: Entitlement id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                product_ids:
                  type: array
                  items:
                    type: string
                  minItems: 1
                  maxItems: 50
              required:
                - product_ids
      responses:
        "200":
          description: The entitlement with its products.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Entitlement"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/entitlements.ts
      x-scopes:
        - project_configuration:entitlements:read_write
  /v2/projects/{project_id}/offerings:
    get:
      operationId: listOfferings
      tags:
        - Offerings
      summary: List offerings
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: expand
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - items.package
                - items.package.product
          style: form
          explode: true
          description: Embed packages, and their products.
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Offering"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:offerings:read
    post:
      operationId: createOffering
      tags:
        - Offerings
      summary: Create an offering
      description: The project's first offering becomes current.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                lookup_key:
                  type: string
                  maxLength: 200
                display_name:
                  type: string
                  maxLength: 1500
                metadata:
                  type:
                    - object
                    - "null"
              required:
                - lookup_key
                - display_name
            example:
              lookup_key: default
              display_name: Standard plans
      responses:
        "201":
          description: The offering.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Offering"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "409":
          $ref: "#/components/responses/V2Error409"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:offerings:read_write
  /v2/projects/{project_id}/offerings/{offering_id}:
    get:
      operationId: getOffering
      tags:
        - Offerings
      summary: Get an offering
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: offering_id
          in: path
          required: true
          schema:
            type: string
          description: Offering id (ofrng...).
        - name: expand
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - package
                - package.product
          style: form
          explode: true
          description: Embed packages, and their products.
      responses:
        "200":
          description: The offering.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Offering"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:offerings:read
    post:
      operationId: updateOffering
      tags:
        - Offerings
      summary: Update an offering or make it current
      description: "`is_current: true` makes it the only current offering. An archived offering cannot be made current (422)."
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: offering_id
          in: path
          required: true
          schema:
            type: string
          description: Offering id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                display_name:
                  type: string
                is_current:
                  type: boolean
                metadata:
                  type:
                    - object
                    - "null"
            example:
              is_current: true
      responses:
        "200":
          description: The offering.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Offering"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "422":
          $ref: "#/components/responses/V2Error422"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:offerings:read_write
    delete:
      operationId: deleteOffering
      tags:
        - Offerings
      summary: Delete an offering
      description: Deletes its packages and clears customer overrides that point to it.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: offering_id
          in: path
          required: true
          schema:
            type: string
          description: Offering id.
      responses:
        "200":
          description: Deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deleted"
              example:
                object: offering
                id: …
                deleted_at: 1790801342625
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:offerings:read_write
  /v2/projects/{project_id}/offerings/{offering_id}/actions/archive:
    post:
      operationId: archiveOffering
      tags:
        - Offerings
      summary: Archive an offering
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: offering_id
          in: path
          required: true
          schema:
            type: string
          description: Offering id.
      responses:
        "200":
          description: The archived offering.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Offering"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "422":
          $ref: "#/components/responses/V2Error422"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:offerings:read_write
  /v2/projects/{project_id}/offerings/{offering_id}/actions/unarchive:
    post:
      operationId: unarchiveOffering
      tags:
        - Offerings
      summary: Unarchive an offering
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: offering_id
          in: path
          required: true
          schema:
            type: string
          description: Offering id.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                unarchive_referenced_entities:
                  type: boolean
                  description: Also unarchive the products in its packages.
      responses:
        "200":
          description: The offering.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Offering"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:offerings:read_write
  /v2/projects/{project_id}/offerings/{offering_id}/packages:
    get:
      operationId: listPackages
      tags:
        - Packages
      summary: List an offering's packages
      description: "Ordered by position, then creation: the order the SDK shows them in."
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: offering_id
          in: path
          required: true
          schema:
            type: string
          description: Offering id.
        - name: expand
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - items.product
          style: form
          explode: true
          description: Embed products.
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Package"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:packages:read
    post:
      operationId: createPackage
      tags:
        - Packages
      summary: Create a package
      description: Use the standard lookup keys (`$rc_monthly`, `$rc_annual`, `$rc_weekly`, `$rc_lifetime` ...) so the SDK's convenience accessors work. Without `position`, the package goes last.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: offering_id
          in: path
          required: true
          schema:
            type: string
          description: Offering id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                lookup_key:
                  type: string
                display_name:
                  type: string
                position:
                  type: integer
                  minimum: 0
              required:
                - lookup_key
                - display_name
            example:
              lookup_key: $rc_monthly
              display_name: Monthly
              position: 0
      responses:
        "201":
          description: The package.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Package"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "409":
          $ref: "#/components/responses/V2Error409"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:packages:read_write
  /v2/projects/{project_id}/packages/{package_id}:
    get:
      operationId: getPackage
      tags:
        - Packages
      summary: Get a package
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: package_id
          in: path
          required: true
          schema:
            type: string
          description: Package id (pkge...).
        - name: expand
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - product
          style: form
          explode: true
          description: Embed products.
      responses:
        "200":
          description: The package.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Package"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:packages:read
    post:
      operationId: updatePackage
      tags:
        - Packages
      summary: Update a package
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: package_id
          in: path
          required: true
          schema:
            type: string
          description: Package id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                display_name:
                  type: string
                position:
                  type: integer
                  minimum: 0
      responses:
        "200":
          description: The package.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Package"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:packages:read_write
    delete:
      operationId: deletePackage
      tags:
        - Packages
      summary: Delete a package
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: package_id
          in: path
          required: true
          schema:
            type: string
          description: Package id.
      responses:
        "200":
          description: Deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deleted"
              example:
                object: package
                id: …
                deleted_at: 1790801342625
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:packages:read_write
  /v2/projects/{project_id}/packages/{package_id}/products:
    get:
      operationId: listPackageProducts
      tags:
        - Packages
      summary: List a package's products
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: package_id
          in: path
          required: true
          schema:
            type: string
          description: Package id.
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/PackageProduct"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:packages:read
  /v2/projects/{project_id}/packages/{package_id}/actions/attach_products:
    post:
      operationId: attachPackageProducts
      tags:
        - Packages
      summary: Attach products to a package
      description: One product per app, so each app's SDK finds its product. Two products of the same app can share a package only with non-overlapping `eligibility_criteria` (409 otherwise).
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: package_id
          in: path
          required: true
          schema:
            type: string
          description: Package id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                products:
                  type: array
                  items:
                    type: object
                    properties:
                      product_id:
                        type: string
                      eligibility_criteria:
                        type: string
                        enum:
                          - all
                          - google_sdk_lt_6
                          - google_sdk_ge_6
                    required:
                      - product_id
                      - eligibility_criteria
                  minItems: 1
                  maxItems: 50
              required:
                - products
            example:
              products:
                - product_id: prode0zhpfisko
                  eligibility_criteria: all
      responses:
        "200":
          description: The package with its products.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Package"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "409":
          $ref: "#/components/responses/V2Error409"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:packages:read_write
  /v2/projects/{project_id}/packages/{package_id}/actions/detach_products:
    post:
      operationId: detachPackageProducts
      tags:
        - Packages
      summary: Detach products from a package
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: package_id
          in: path
          required: true
          schema:
            type: string
          description: Package id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                product_ids:
                  type: array
                  items:
                    type: string
                  minItems: 1
                  maxItems: 50
              required:
                - product_ids
      responses:
        "200":
          description: The package with its products.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Package"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/offerings.ts
      x-scopes:
        - project_configuration:packages:read_write
  /v2/projects/{project_id}/customers:
    get:
      operationId: listCustomers
      tags:
        - Customers
      summary: List or search customers
      description: Newest first (by first seen).
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: search
          in: query
          schema:
            type: string
          description: Exact match on an app user id, the `$email` attribute (any case) or a store transaction id.
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Customer"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:customers:read
    post:
      operationId: createCustomer
      tags:
        - Customers
      summary: Create a customer
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: string
                  description: App user id.
                  maxLength: 1500
                attributes:
                  type: array
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                      value:
                        type: string
                    required:
                      - name
                      - value
                  maxItems: 500
              required:
                - id
            example:
              id: user_42
              attributes:
                - name: $email
                  value: ana@example.com
      responses:
        "201":
          description: The customer.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Customer"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "409":
          $ref: "#/components/responses/V2Error409"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:customers:read_write
  /v2/projects/{project_id}/customers/{customer_id}:
    get:
      operationId: getCustomer
      tags:
        - Customers
      summary: Get a customer
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/CustomerId"
        - name: expand
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - attributes
          style: form
          explode: true
          description: "`attributes` embeds the customer's attributes."
      responses:
        "200":
          description: The customer.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Customer"
              example:
                object: customer
                id: user_1
                project_id: proj18pzzkao
                first_seen_at: 1790800914012
                last_seen_at: 1790800914034
                last_seen_app_version: null
                last_seen_country: null
                last_seen_platform: null
                last_seen_platform_version: null
                active_entitlements:
                  object: list
                  items:
                    - object: customer.active_entitlement
                      entitlement_id: entl1v0bp6r0qs
                      expires_at: 1793392914000
                  next_page: null
                  url: /v2/projects/proj18pzzkao/customers/user_1/active_entitlements
                experiment: null
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:customers:read
    delete:
      operationId: deleteCustomer
      tags:
        - Customers
      summary: Delete a customer
      description: Deletes aliases, attributes, subscriptions, purchases, transactions and events. Cannot be undone.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/CustomerId"
      responses:
        "200":
          description: Deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deleted"
              example:
                object: customer
                id: …
                deleted_at: 1790801342625
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:customers:read_write
  /v2/projects/{project_id}/customers/{customer_id}/aliases:
    get:
      operationId: listAliases
      tags:
        - Customers
      summary: List a customer's app user ids
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/CustomerId"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/CustomerAlias"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:customers:read
  /v2/projects/{project_id}/customers/{customer_id}/attributes:
    get:
      operationId: listAttributes
      tags:
        - Customers
      summary: List a customer's attributes
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/CustomerId"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/CustomerAttribute"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:customers:read
    post:
      operationId: setAttributes
      tags:
        - Customers
      summary: Set a customer's attributes
      description: API writes always win over older SDK writes. A null value deletes the attribute.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/CustomerId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                attributes:
                  type: array
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                      value:
                        type:
                          - string
                          - "null"
                    required:
                      - name
                      - value
                  minItems: 1
                  maxItems: 500
              required:
                - attributes
            example:
              attributes:
                - name: $displayName
                  value: Ana
      responses:
        "200":
          description: Every attribute of the customer.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/CustomerAttribute"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:customers:read_write
  /v2/projects/{project_id}/customers/{customer_id}/active_entitlements:
    get:
      operationId: listActiveEntitlements
      tags:
        - Customers
      summary: List a customer's active entitlements
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/CustomerId"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/ActiveEntitlement"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:customers:read
  /v2/projects/{project_id}/customers/{customer_id}/subscriptions:
    get:
      operationId: listCustomerSubscriptions
      tags:
        - Customers
      summary: List a customer's subscriptions
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/CustomerId"
        - $ref: "#/components/parameters/Environment"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of subscriptions.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Subscription"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
              example:
                object: list
                items:
                  - object: subscription
                    id: sub_k1u15wepvw0dfh25
                    customer_id: user_1
                    original_customer_id: user_1
                    product_id: prode0zhpfisko
                    starts_at: 1790800914000
                    current_period_starts_at: 1790800914000
                    current_period_ends_at: 1793392914000
                    ends_at: 1793392914000
                    gives_access: true
                    pending_payment: false
                    auto_renewal_status: will_renew
                    status: active
                    total_revenue_in_usd:
                      currency: USD
                      gross: 9.99
                      commission: 0
                      tax: 0
                      proceeds: 9.99
                    presented_offering_id: null
                    entitlements:
                      object: list
                      items:
                        - object: entitlement
                          id: entl1v0bp6r0qs
                          project_id: proj18pzzkao
                          lookup_key: pro
                          display_name: Pro access
                          created_at: 1790800901115
                          state: active
                      next_page: null
                      url: /v2/projects/proj18pzzkao/subscriptions/sub_k1u15wepvw0dfh25/entitlements
                    environment: sandbox
                    store: test_store
                    store_subscription_identifier: test_1790800914000_quickstart
                    ownership: purchased
                    management_url: null
                next_page: null
                url: /v2/projects/proj18pzzkao/customers/user_1/subscriptions
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:subscriptions:read
  /v2/projects/{project_id}/customers/{customer_id}/purchases:
    get:
      operationId: listCustomerPurchases
      tags:
        - Customers
      summary: List a customer's one-time purchases
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/CustomerId"
        - $ref: "#/components/parameters/Environment"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Purchase"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:purchases:read
  /v2/projects/{project_id}/customers/{customer_id}/events:
    get:
      operationId: listCustomerEvents
      tags:
        - Customers
      summary: List a customer's events
      description: Newest first. `body` is the webhook event.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/CustomerId"
        - $ref: "#/components/parameters/Environment"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/CustomerEvent"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:customers:read
  /v2/projects/{project_id}/customers/{customer_id}/actions/grant_entitlement:
    post:
      operationId: grantEntitlement
      tags:
        - Customers
      summary: Grant an entitlement
      description: Promotional access until `expires_at`. A grant ending within 2 hours of an existing grant for the same entitlement changes nothing.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/CustomerId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                entitlement_id:
                  type: string
                  description: Entitlement id (entl...).
                expires_at:
                  type: integer
                  description: Epoch milliseconds, in the future.
              required:
                - entitlement_id
                - expires_at
            example:
              entitlement_id: entl1v0bp6r0qs
              expires_at: 1830000000000
      responses:
        "201":
          description: The customer.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Customer"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:customers:read_write
  /v2/projects/{project_id}/customers/{customer_id}/actions/revoke_granted_entitlement:
    post:
      operationId: revokeGrantedEntitlement
      tags:
        - Customers
      summary: Revoke a granted entitlement
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/CustomerId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                entitlement_id:
                  type: string
              required:
                - entitlement_id
      responses:
        "200":
          description: The customer.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Customer"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:customers:read_write
  /v2/projects/{project_id}/customers/{customer_id}/actions/assign_offering:
    post:
      operationId: assignOffering
      tags:
        - Customers
      summary: Assign an offering to a customer
      description: The customer sees this offering as current. `null` removes the override.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/CustomerId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                offering_id:
                  type:
                    - string
                    - "null"
                  description: Offering id (ofrng...) or null.
              required:
                - offering_id
            example:
              offering_id: ofrngjfr71v5awb
      responses:
        "200":
          description: Done.
          content:
            application/json:
              schema:
                type: object
              example: {}
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - project_configuration:offerings:read
        - customer_information:customers:read_write
  /v2/projects/{project_id}/subscriptions:
    get:
      operationId: searchSubscriptions
      tags:
        - Subscriptions
      summary: Find subscriptions by store id
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: store_subscription_identifier
          in: query
          required: true
          schema:
            type: string
          description: Store transaction id, original transaction id or purchase token.
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Subscription"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:subscriptions:read
  /v2/projects/{project_id}/subscriptions/{subscription_id}:
    get:
      operationId: getSubscription
      tags:
        - Subscriptions
      summary: Get a subscription
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
          description: Subscription id (sub_...).
      responses:
        "200":
          description: The subscription.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Subscription"
              example:
                object: subscription
                id: sub_k1u15wepvw0dfh25
                customer_id: user_1
                original_customer_id: user_1
                product_id: prode0zhpfisko
                starts_at: 1790800914000
                current_period_starts_at: 1790800914000
                current_period_ends_at: 1793392914000
                ends_at: 1793392914000
                gives_access: true
                pending_payment: false
                auto_renewal_status: will_renew
                status: active
                total_revenue_in_usd:
                  currency: USD
                  gross: 9.99
                  commission: 0
                  tax: 0
                  proceeds: 9.99
                presented_offering_id: null
                entitlements:
                  object: list
                  items:
                    - object: entitlement
                      id: entl1v0bp6r0qs
                      project_id: proj18pzzkao
                      lookup_key: pro
                      display_name: Pro access
                      created_at: 1790800901115
                      state: active
                  next_page: null
                  url: /v2/projects/proj18pzzkao/subscriptions/sub_k1u15wepvw0dfh25/entitlements
                environment: sandbox
                store: test_store
                store_subscription_identifier: test_1790800914000_quickstart
                ownership: purchased
                management_url: null
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:subscriptions:read
  /v2/projects/{project_id}/subscriptions/{subscription_id}/entitlements:
    get:
      operationId: listSubscriptionEntitlements
      tags:
        - Subscriptions
      summary: List the entitlements a subscription unlocks
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
          description: Subscription id.
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Entitlement"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:subscriptions:read
  /v2/projects/{project_id}/subscriptions/{subscription_id}/transactions:
    get:
      operationId: listSubscriptionTransactions
      tags:
        - Subscriptions
      summary: List a subscription's payments
      description: One item per paid store transaction of the subscription (purchase, trial start, renewal). A refunded payment's `effective_expiration_date` is the refund time.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
          description: Subscription id.
        - name: sort
          in: query
          schema:
            type: string
            enum:
              - id
              - purchased_at
          description: Default id.
        - name: direction
          in: query
          schema:
            type: string
            enum:
              - asc
              - desc
          description: Default asc.
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/SubscriptionTransaction"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:subscriptions:read
  /v2/projects/{project_id}/subscriptions/{subscription_id}/actions/cancel:
    post:
      operationId: cancelSubscription
      tags:
        - Subscriptions
      summary: Cancel a subscription (Google Play)
      description: "Google Play only: turns auto-renew off. Other stores answer 422."
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
          description: Subscription id.
      responses:
        "200":
          description: The subscription.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Subscription"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "422":
          $ref: "#/components/responses/V2Error422"
        "503":
          $ref: "#/components/responses/V2Error503"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:subscriptions:read_write
  /v2/projects/{project_id}/subscriptions/{subscription_id}/actions/refund:
    post:
      operationId: refundSubscription
      tags:
        - Subscriptions
      summary: Refund and revoke a subscription (Google Play)
      description: "Google Play only: refunds the latest payment and ends access now. App Store refunds go through Apple. Other stores answer 422."
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
          description: Subscription id.
      responses:
        "200":
          description: The subscription.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Subscription"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "422":
          $ref: "#/components/responses/V2Error422"
        "503":
          $ref: "#/components/responses/V2Error503"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:subscriptions:read_write
  /v2/projects/{project_id}/subscriptions/{subscription_id}/actions/extend:
    post:
      operationId: extendSubscription
      tags:
        - Subscriptions
      summary: Extend a subscription
      description: "App Store: Apple extends the renewal date (1 to 90 days, `extend_reason_code` required, needs the in-app purchase key). Google Play: the renewal is deferred (up to 365 days). Send `extend_by_days` or `extend_until_ms`, not both."
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
          description: Subscription id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                  properties:
                    extend_by_days:
                      type: integer
                      minimum: 1
                    extend_reason_code:
                      type: string
                      enum:
                        - undeclared
                        - customer_satisfaction
                        - other
                        - service_issue_or_outage
                      description: Apple's reason for the extension. Required for App Store subscriptions.
                  required:
                    - extend_by_days
                  additionalProperties: false
                - type: object
                  properties:
                    extend_until_ms:
                      type: integer
                      description: New end, epoch milliseconds.
                    extend_reason_code:
                      type: string
                      enum:
                        - undeclared
                        - customer_satisfaction
                        - other
                        - service_issue_or_outage
                      description: Apple's reason for the extension. Required for App Store subscriptions.
                  required:
                    - extend_until_ms
                  additionalProperties: false
            example:
              extend_by_days: 7
              extend_reason_code: customer_satisfaction
      responses:
        "200":
          description: The subscription.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Subscription"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "422":
          $ref: "#/components/responses/V2Error422"
        "503":
          $ref: "#/components/responses/V2Error503"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:subscriptions:read_write
  /v2/projects/{project_id}/subscriptions/{subscription_id}/transactions/{transaction_id}/actions/refund:
    post:
      operationId: refundSubscriptionTransaction
      tags:
        - Subscriptions
      summary: Refund one payment of a subscription (Google Play)
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
          description: Subscription id.
        - name: transaction_id
          in: path
          required: true
          schema:
            type: string
          description: Google order id of the payment.
      responses:
        "200":
          description: The refunded payment.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionTransaction"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "422":
          $ref: "#/components/responses/V2Error422"
        "503":
          $ref: "#/components/responses/V2Error503"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:subscriptions:read_write
  /v2/projects/{project_id}/purchases:
    get:
      operationId: searchPurchases
      tags:
        - Purchases
      summary: Find one-time purchases by store id
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: store_purchase_identifier
          in: query
          required: true
          schema:
            type: string
          description: Store transaction id.
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Purchase"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:purchases:read
  /v2/projects/{project_id}/purchases/{purchase_id}:
    get:
      operationId: getPurchase
      tags:
        - Purchases
      summary: Get a one-time purchase
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: purchase_id
          in: path
          required: true
          schema:
            type: string
          description: Purchase id.
      responses:
        "200":
          description: The purchase.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Purchase"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:purchases:read
  /v2/projects/{project_id}/purchases/{purchase_id}/entitlements:
    get:
      operationId: listPurchaseEntitlements
      tags:
        - Purchases
      summary: List the entitlements a purchase unlocks
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: purchase_id
          in: path
          required: true
          schema:
            type: string
          description: Purchase id.
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Entitlement"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:purchases:read
  /v2/projects/{project_id}/purchases/{purchase_id}/actions/refund:
    post:
      operationId: refundPurchase
      tags:
        - Purchases
      summary: Refund a one-time purchase (Google Play)
      description: Google Play refunds and revokes the order. Other stores answer 422.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: purchase_id
          in: path
          required: true
          schema:
            type: string
          description: Purchase id.
      responses:
        "200":
          description: The purchase.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Purchase"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "422":
          $ref: "#/components/responses/V2Error422"
        "503":
          $ref: "#/components/responses/V2Error503"
      x-source: routes/v2/customers.ts
      x-scopes:
        - customer_information:purchases:read_write
  /v2/projects/{project_id}/metrics/overview:
    get:
      operationId: getOverviewMetrics
      tags:
        - Metrics
      summary: Overview metrics
      description: "Computed live: active trials, active paid subscriptions, MRR (USD price normalised to a month), revenue in the last 28 days, new and active customers in the last 28 days."
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: currency
          in: query
          schema:
            type: string
            const: USD
          description: Only USD is supported.
        - name: environment
          in: query
          schema:
            type: string
            enum:
              - production
              - sandbox
          description: RevenueDot extension. Default production.
      responses:
        "200":
          description: The metrics.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OverviewMetrics"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/metrics.ts
      x-scopes:
        - charts_metrics:overview:read
  /v2/projects/{project_id}/integrations/webhooks:
    get:
      operationId: listWebhooks
      tags:
        - Webhook integrations
      summary: List webhooks
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/WebhookIntegration"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/integrations.ts
      x-scopes:
        - project_configuration:integrations:read
    post:
      operationId: createWebhook
      tags:
        - Webhook integrations
      summary: Create a webhook
      description: "The answer includes `signing_secret` (whsec_...) once. Store it: it verifies the `X-RevenueCat-Webhook-Signature` header. See [Webhooks](../docs/guides/webhooks.md)."
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  maxLength: 255
                url:
                  type: string
                  description: http(s) URL.
                  format: uri
                authorization_header:
                  type:
                    - string
                    - "null"
                  description: Sent as the Authorization header.
                environment:
                  type:
                    - string
                    - "null"
                  enum:
                    - production
                    - sandbox
                    - null
                  description: "Null or absent: both."
                event_types:
                  type: array
                  items:
                    type: string
                    enum:
                      - initial_purchase
                      - renewal
                      - product_change
                      - cancellation
                      - billing_issue
                      - non_renewing_purchase
                      - uncancellation
                      - transfer
                      - subscription_paused
                      - expiration
                      - subscription_extended
                      - invoice_issuance
                      - temporary_entitlement_grant
                      - refund_reversed
                      - virtual_currency_transaction
                      - test
                      - experiment_enrollment
                      - purchase_redeemed
                      - subscriber_alias
                      - price_increase_consent_required
                      - price_increase_consent_approved
                  description: "Empty or absent: every type."
                app_id:
                  type:
                    - string
                    - "null"
                  description: Only this app's events.
              required:
                - name
                - url
            example:
              name: Backend
              url: https://api.example.com/webhooks/revenuedot
              authorization_header: Bearer my-shared-token
              environment: production
              event_types:
                - initial_purchase
                - renewal
      responses:
        "201":
          description: The webhook with its signing secret.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookIntegration"
              example:
                object: webhook_integration
                id: wh_ceps8nr7mczvhaqw
                project_id: proj18pzzkao
                name: Backend
                url: https://api.example.com/webhooks/revenuedot
                environment: production
                event_types:
                  - initial_purchase
                  - renewal
                app_id: null
                created_at: 1790801342625
                signing_secret: whsec_3f5b7fb5a591c17aaa9108376df0bddbe1555f0a908bfdc0
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/integrations.ts
      x-scopes:
        - project_configuration:integrations:read_write
  /v2/projects/{project_id}/integrations/webhooks/{webhook_integration_id}:
    get:
      operationId: getWebhook
      tags:
        - Webhook integrations
      summary: Get a webhook
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: webhook_integration_id
          in: path
          required: true
          schema:
            type: string
          description: Webhook id (wh_...).
      responses:
        "200":
          description: The webhook.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookIntegration"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/integrations.ts
      x-scopes:
        - project_configuration:integrations:read
    post:
      operationId: updateWebhook
      tags:
        - Webhook integrations
      summary: Update a webhook
      description: "`enabled` is a RevenueDot extension: false pauses deliveries without deleting the webhook. Events recorded while it is off are not sent; queued retries resume when it is turned on. Read it with `GET /v2/projects/{project_id}/webhooks`."
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: webhook_integration_id
          in: path
          required: true
          schema:
            type: string
          description: Webhook id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                url:
                  type: string
                authorization_header:
                  type:
                    - string
                    - "null"
                environment:
                  type:
                    - string
                    - "null"
                event_types:
                  type: array
                  items:
                    type: string
                app_id:
                  type:
                    - string
                    - "null"
                enabled:
                  type: boolean
                  description: RevenueDot extension. False pauses deliveries.
            example:
              enabled: false
      responses:
        "200":
          description: The webhook.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookIntegration"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/integrations.ts
      x-scopes:
        - project_configuration:integrations:read_write
    delete:
      operationId: deleteWebhook
      tags:
        - Webhook integrations
      summary: Delete a webhook
      description: Pending deliveries are deleted with it.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: webhook_integration_id
          in: path
          required: true
          schema:
            type: string
          description: Webhook id.
      responses:
        "200":
          description: Deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deleted"
              example:
                object: webhook_integration
                id: …
                deleted_at: 1790801342625
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/integrations.ts
      x-scopes:
        - project_configuration:integrations:read_write
  /auth/config:
    get:
      operationId: authConfig
      tags:
        - Dashboard auth
      summary: Whether sign-up is open
      description: What the sign-in page needs before it shows a form.
      security: []
      responses:
        "200":
          description: The config.
          content:
            application/json:
              schema:
                type: object
                properties:
                  edition:
                    type: string
                    enum:
                      - cloud
                      - self-hosted
                  signup:
                    type: string
                    enum:
                      - open
                      - closed
              example:
                edition: self-hosted
                signup: closed
      x-source: routes/auth.ts
      x-revenuedot-extension: true
  /auth/signup:
    post:
      operationId: signup
      tags:
        - Dashboard auth
      summary: Create a dashboard account
      description: |-
        Creates the user and a first project, and sets the `rd_session` cookie (30 days; `Secure` over https). On a self-hosted server only the first account (the owner) can sign up, unless the server runs with `REVENUEDOT_ALLOW_SIGNUP=true`.

        With `invite_token` (from an invite link), the account joins the inviting project instead of getting a new one, and sign-up works even where it is closed. The email must be the invited address; the account counts as verified. On RevenueDot Cloud, an account without an invite gets an email with a confirmation link (valid 24 hours).
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                  minLength: 8
                  maxLength: 200
                name:
                  type: string
                  maxLength: 100
                project_name:
                  type: string
                  description: "Default: My project. Ignored with `invite_token`."
                  maxLength: 100
                invite_token:
                  type: string
                  description: RevenueDot extension. The token from an invite link (`/invite?token=...`).
                  maxLength: 200
              required:
                - email
                - password
            example:
              email: dev@example.com
              password: change-me-please
              project_name: My app
      responses:
        "201":
          description: Signed up and signed in. With an invite, `project_id` is the project joined.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  project_id:
                    type: string
                    description: Only with `invite_token`.
              example:
                ok: true
        "400":
          description: Invalid email or password, or an invite that is not valid (`invite_invalid`) or for another address (`invite_email_mismatch`).
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
        "403":
          description: "Sign-up is closed: the server has an owner already."
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
              example:
                type: signup_closed
                message: "Sign-up is closed on this server: it has an owner account already. The owner can open it by setting REVENUEDOT_ALLOW_SIGNUP=true."
        "409":
          description: The email is taken.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
      x-source: routes/auth.ts
      x-revenuedot-extension: true
  /auth/login:
    post:
      operationId: login
      tags:
        - Dashboard auth
      summary: Sign in
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                password:
                  type: string
              required:
                - email
                - password
      responses:
        "200":
          description: Signed in; `rd_session` is set.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
              example:
                ok: true
        "400":
          description: Missing fields.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
        "401":
          description: Wrong email or password.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
              example:
                type: authentication_error
                message: Email or password is incorrect.
      x-source: routes/auth.ts
      x-revenuedot-extension: true
  /.well-known/oauth-authorization-server:
    get:
      operationId: oauthMetadata
      tags:
        - OAuth for MCP clients
      summary: OAuth authorization server metadata
      description: RFC 8414 metadata for MCP clients (Claude, ChatGPT, Cursor ...). Endpoints are built from this server's public origin.
      security: []
      responses:
        "200":
          description: Metadata.
          content:
            application/json:
              schema:
                type: object
                properties:
                  issuer:
                    type: string
                  authorization_endpoint:
                    type: string
                  token_endpoint:
                    type: string
                  registration_endpoint:
                    type: string
                  scopes_supported:
                    type: array
                    items:
                      type: string
                  response_types_supported:
                    type: array
                    items:
                      type: string
                  response_modes_supported:
                    type: array
                    items:
                      type: string
                  grant_types_supported:
                    type: array
                    items:
                      type: string
                  token_endpoint_auth_methods_supported:
                    type: array
                    items:
                      type: string
                  code_challenge_methods_supported:
                    type: array
                    items:
                      type: string
                  service_documentation:
                    type: string
              example:
                issuer: https://revenuedot.example.com
                authorization_endpoint: https://revenuedot.example.com/oauth/authorize
                token_endpoint: https://revenuedot.example.com/oauth/token
                registration_endpoint: https://revenuedot.example.com/oauth/register
                scopes_supported:
                  - project:read
                  - project:write
                response_types_supported:
                  - code
                response_modes_supported:
                  - query
                grant_types_supported:
                  - authorization_code
                token_endpoint_auth_methods_supported:
                  - none
                code_challenge_methods_supported:
                  - S256
                service_documentation: https://revenuedot.app/docs/mcp
      x-source: routes/oauth.ts
      x-revenuedot-extension: true
  /oauth/register:
    post:
      operationId: oauthRegister
      tags:
        - OAuth for MCP clients
      summary: Register an OAuth client
      description: Dynamic client registration (RFC 7591), public clients only. Redirect URIs must be https, http on localhost, or an app scheme such as cursor://.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                redirect_uris:
                  type: array
                  items:
                    type: string
                  minItems: 1
                  maxItems: 10
                client_name:
                  type: string
                grant_types:
                  type: array
                  items:
                    type: string
              required:
                - redirect_uris
            example:
              client_name: Claude
              redirect_uris:
                - https://claude.ai/api/mcp/auth_callback
      responses:
        "201":
          description: The client.
          content:
            application/json:
              schema:
                type: object
                properties:
                  client_id:
                    type: string
                    description: oac_...
                  client_id_issued_at:
                    type: integer
                  client_name:
                    type: string
                  redirect_uris:
                    type: array
                    items:
                      type: string
                  grant_types:
                    type: array
                    items:
                      type: string
                  response_types:
                    type: array
                    items:
                      type: string
                  token_endpoint_auth_method:
                    type: string
                    const: none
        "400":
          description: Invalid metadata or redirect URI.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  error_description:
                    type: string
      x-source: routes/oauth.ts
      x-revenuedot-extension: true
  /oauth/authorize:
    get:
      operationId: oauthAuthorize
      tags:
        - OAuth for MCP clients
      summary: Consent screen
      description: An HTML page. The user signs in to the dashboard (the session cookie is reused), picks one project and read or read-write access. PKCE with S256 is required.
      security: []
      parameters:
        - name: response_type
          in: query
          schema:
            type: string
        - name: client_id
          in: query
          schema:
            type: string
        - name: redirect_uri
          in: query
          schema:
            type: string
        - name: state
          in: query
          schema:
            type: string
        - name: scope
          in: query
          schema:
            type: string
        - name: code_challenge
          in: query
          schema:
            type: string
        - name: code_challenge_method
          in: query
          schema:
            type: string
        - name: resource
          in: query
          schema:
            type: string
      responses:
        "200":
          description: The consent page.
          content:
            text/html:
              schema:
                type: string
        "302":
          description: Back to the client with an error.
        "400":
          description: Unknown client or redirect URI.
          content:
            text/html:
              schema:
                type: string
      x-source: routes/oauth.ts
      x-revenuedot-extension: true
    post:
      operationId: oauthDecide
      tags:
        - OAuth for MCP clients
      summary: Submit the consent decision
      description: The consent form posts here. On allow, redirects to the client's redirect URI with a one-time `code` (valid 10 minutes).
      security:
        - dashboardSession: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                decision:
                  type: string
                  enum:
                    - allow
                    - deny
                project_id:
                  type: string
                access:
                  type: string
                  enum:
                    - project:read
                    - project:write
                csrf:
                  type: string
      responses:
        "302":
          description: Redirect with `code` and `state`, or with `error`.
        "403":
          description: The form expired.
          content:
            text/html:
              schema:
                type: string
      x-source: routes/oauth.ts
      x-revenuedot-extension: true
  /oauth/token:
    post:
      operationId: oauthToken
      tags:
        - OAuth for MCP clients
      summary: Exchange a code for an access token
      description: authorization_code grant with PKCE. The access token is a secret API key (sk_...) bound to the chosen project with the approved permissions. It does not expire; revoke it on the project's API keys page.
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                grant_type:
                  type: string
                  const: authorization_code
                code:
                  type: string
                code_verifier:
                  type: string
                client_id:
                  type: string
                redirect_uri:
                  type: string
              required:
                - grant_type
                - code
                - code_verifier
          application/json:
            schema:
              type: object
              properties:
                grant_type:
                  type: string
                code:
                  type: string
                code_verifier:
                  type: string
                client_id:
                  type: string
                redirect_uri:
                  type: string
      responses:
        "200":
          description: The token.
          content:
            application/json:
              schema:
                type: object
                properties:
                  access_token:
                    type: string
                    description: A secret key, sk_...
                  token_type:
                    type: string
                    const: Bearer
                  scope:
                    type: string
                    enum:
                      - project:read
                      - project:write
                  project_id:
                    type: string
        "400":
          description: Invalid grant or request.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  error_description:
                    type: string
      x-source: routes/oauth.ts
      x-revenuedot-extension: true
  /auth/logout:
    post:
      operationId: logout
      tags:
        - Dashboard auth
      summary: Sign out
      security:
        - dashboardSession: []
      responses:
        "200":
          description: Signed out.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
              example:
                ok: true
      x-source: routes/auth.ts
      x-revenuedot-extension: true
  /auth/me:
    get:
      operationId: me
      tags:
        - Dashboard auth
      summary: The signed-in user and their projects
      security:
        - dashboardSession: []
      responses:
        "200":
          description: The user.
          content:
            application/json:
              schema:
                type: object
                properties:
                  user:
                    type: object
                    properties:
                      id:
                        type: string
                      email:
                        type: string
                      name:
                        type:
                          - string
                          - "null"
                      email_verified:
                        type: boolean
                        description: Whether the user confirmed their email address. Always true for accounts created from an invite or after a password reset.
                      alert_emails:
                        type: boolean
                        description: Whether the user gets alert emails for projects they administer.
                  account:
                    type: object
                    properties:
                      edition:
                        type: string
                        enum:
                          - cloud
                          - self-hosted
                      plan:
                        type: string
                        description: The account plan (`free` on RevenueDot Cloud).
                      email_verification_required:
                        type: boolean
                        description: True on RevenueDot Cloud until the user confirms their email. Until then they cannot invite people or create secret API keys. Always false on a self-hosted server.
                  projects:
                    type: array
                    items:
                      type: object
              example:
                user:
                  id: usr_8k2m4q
                  email: dev@example.com
                  name: Dana
                  email_verified: true
                  alert_emails: true
                account:
                  edition: cloud
                  plan: free
                  email_verification_required: false
                projects: []
        "401":
          description: Not signed in.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
              example:
                type: authentication_error
                message: Not signed in.
      x-source: routes/auth.ts
      x-revenuedot-extension: true
    post:
      operationId: updateMe
      tags:
        - Dashboard auth
      summary: Update account settings
      description: The display name and whether the user gets [alert emails](../docs/guides/alerts.md) for projects they administer. Send only the fields to change. A null or empty `name` clears it.
      security:
        - dashboardSession: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type:
                    - string
                    - "null"
                  maxLength: 100
                alert_emails:
                  type: boolean
                  description: False stops alert emails for every project.
            example:
              alert_emails: false
      responses:
        "200":
          description: The updated user.
          content:
            application/json:
              schema:
                type: object
                properties:
                  user:
                    type: object
                    properties:
                      id:
                        type: string
                      email:
                        type: string
                      name:
                        type:
                          - string
                          - "null"
                      email_verified:
                        type: boolean
                        description: Whether the user confirmed their email address. Always true for accounts created from an invite or after a password reset.
                      alert_emails:
                        type: boolean
                        description: Whether the user gets alert emails for projects they administer.
              example:
                user:
                  id: usr_8k2m4q
                  email: dev@example.com
                  name: Dana
                  email_verified: true
                  alert_emails: false
        "400":
          description: Invalid field.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
        "401":
          description: Not signed in.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
              example:
                type: authentication_error
                message: Not signed in.
      x-source: routes/auth.ts
      x-revenuedot-extension: true
  /auth/password/forgot:
    post:
      operationId: forgotPassword
      tags:
        - Dashboard auth
      summary: Email a password reset link
      description: |-
        Always answers 200 with the same body, whether or not an account uses the address, so the answer does not reveal who has an account. If one does, it gets a link to `/reset-password` that works once and expires after 1 hour.

        Limits: 5 requests per IP address per 15 minutes (then 429), and 3 emails per address per hour (further requests answer 200 but send nothing). See [I forgot my password](../docs/help/forgot-password.md).
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                  maxLength: 320
              required:
                - email
            example:
              email: dev@example.com
      responses:
        "200":
          description: Accepted.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  message:
                    type: string
              example:
                ok: true
                message: If an account uses this email, we sent it a link to reset the password. The link expires in 1 hour.
        "400":
          description: Not a valid email address.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
        "429":
          description: Too many requests from this IP address.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
              example:
                type: rate_limit_error
                message: Too many password reset requests. Try again in 15 minutes.
      x-source: routes/auth.ts
      x-revenuedot-extension: true
  /auth/password/check:
    post:
      operationId: checkPasswordReset
      tags:
        - Dashboard auth
      summary: Check a password reset link
      description: Tells the reset page whether the link still works before the user types a new password. Does not use up the link.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                token:
                  type: string
                  description: The `token` from the reset link.
                  maxLength: 200
              required:
                - token
      responses:
        "200":
          description: Whether the link works.
          content:
            application/json:
              schema:
                type: object
                properties:
                  valid:
                    type: boolean
                  email:
                    type: string
                    description: The account's email, when valid.
                  reason:
                    type: string
                    enum:
                      - invalid
                      - expired
                      - used
                  message:
                    type: string
                    description: Why it does not work.
              example:
                valid: false
                reason: expired
                message: This link has expired. Ask for a new one.
        "400":
          description: Missing token.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
      x-source: routes/auth.ts
      x-revenuedot-extension: true
  /auth/password/reset:
    post:
      operationId: resetPassword
      tags:
        - Dashboard auth
      summary: Set a new password from a reset link
      description: Sets the password, signs the user out on every device, marks the email as confirmed (the link proved the inbox) and signs this browser in with a new `rd_session` cookie. Every other open reset link of the user stops working.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                token:
                  type: string
                  maxLength: 200
                password:
                  type: string
                  minLength: 8
                  maxLength: 200
              required:
                - token
                - password
            example:
              token: …
              password: a-new-long-password
      responses:
        "200":
          description: Password changed and signed in.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
              example:
                ok: true
        "400":
          description: The password is too short or too long, or the link is not valid (`token_invalid` with a `reason`).
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  reason:
                    type: string
                    enum:
                      - invalid
                      - expired
                      - used
                  message:
                    type: string
              example:
                type: token_invalid
                reason: used
                message: This link was already used. Ask for a new one if you still need it.
      x-source: routes/auth.ts
      x-revenuedot-extension: true
  /auth/email/verify:
    post:
      operationId: verifyEmail
      tags:
        - Dashboard auth
      summary: Confirm an email address
      description: "RevenueDot Cloud only: the link in the confirmation email sent at sign-up (valid 24 hours, works once). Self-hosted servers treat every account as confirmed."
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                token:
                  type: string
                  description: The `token` from the confirmation link.
                  maxLength: 200
              required:
                - token
      responses:
        "200":
          description: Confirmed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  email:
                    type: string
              example:
                ok: true
                email: dev@example.com
        "400":
          description: The link is not valid (`token_invalid` with a `reason`).
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  reason:
                    type: string
                    enum:
                      - invalid
                      - expired
                      - used
                  message:
                    type: string
      x-source: routes/auth.ts
      x-revenuedot-extension: true
  /auth/email/verify/resend:
    post:
      operationId: resendVerification
      tags:
        - Dashboard auth
      summary: Send a new confirmation email
      description: "Up to 5 per user per hour. An account that is already confirmed gets `already_verified: true` and no email."
      security:
        - dashboardSession: []
      responses:
        "200":
          description: Sent, or already confirmed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  email:
                    type: string
                  already_verified:
                    type: boolean
              example:
                ok: true
                email: dev@example.com
        "401":
          description: Not signed in.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
              example:
                type: authentication_error
                message: Not signed in.
        "429":
          description: Too many emails this hour.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
              example:
                type: rate_limit_error
                message: Too many emails sent. Try again in an hour.
        "502":
          description: The mail server did not accept the email.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
      x-source: routes/auth.ts
      x-revenuedot-extension: true
  /auth/invites/{token}:
    get:
      operationId: getInvite
      tags:
        - Dashboard auth
      summary: Look up an invite
      description: "What the invite page shows: the project, the role, who sent it and whether the invited address has an account already (sign in and accept, or sign up with `invite_token`)."
      security: []
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
          description: The `token` from the invite link (`/invite?token=...`).
      responses:
        "200":
          description: The invite.
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    type: string
                    const: invite
                  email:
                    type: string
                  role:
                    type: string
                    enum:
                      - admin
                      - developer
                      - viewer
                  project:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
                  invited_by:
                    type:
                      - object
                      - "null"
                    properties:
                      name:
                        type:
                          - string
                          - "null"
                      email:
                        type: string
                  expires_at:
                    type: integer
                    format: int64
                    description: When the link stops working. Epoch milliseconds.
                  account_exists:
                    type: boolean
                    description: Whether an account uses the invited address.
              example:
                object: invite
                email: sam@example.com
                role: developer
                project:
                  id: proj18pzzkao
                  name: My app
                invited_by:
                  name: Dana
                  email: dev@example.com
                expires_at: 1791405714000
                account_exists: false
        "404":
          description: Not valid, expired, already accepted or revoked.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  reason:
                    type: string
                    enum:
                      - invalid
                      - expired
                      - accepted
                      - revoked
                  message:
                    type: string
              example:
                type: invite_invalid
                reason: expired
                message: This invite has expired. Ask the person who invited you for a new one.
      x-source: routes/auth.ts
      x-revenuedot-extension: true
  /auth/invites/{token}/accept:
    post:
      operationId: acceptInvite
      tags:
        - Dashboard auth
      summary: Accept an invite
      description: For a user who already has an account, signed in with the invited address. Adds them to the project with the invite's role; someone who is already a member keeps their role. Also marks their email as confirmed.
      security:
        - dashboardSession: []
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
          description: The `token` from the invite link (`/invite?token=...`).
      responses:
        "200":
          description: Joined.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  project_id:
                    type: string
              example:
                ok: true
                project_id: proj18pzzkao
        "401":
          description: Not signed in.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
              example:
                type: authentication_error
                message: Not signed in.
        "403":
          description: Signed in with another address.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
              example:
                type: invite_email_mismatch
                message: This invite is for sam@example.com, and you are signed in as dev@example.com. Sign in with the invited address.
        "404":
          description: The invite is no longer valid.
          content:
            application/json:
              schema:
                type: object
                properties:
                  type:
                    type: string
                  message:
                    type: string
      x-source: routes/auth.ts
      x-revenuedot-extension: true
  /v2/projects/{project_id}/invites:
    get:
      operationId: listInvites
      tags:
        - Members and invites
      summary: List open invites
      description: "Invites nobody has accepted or revoked, oldest first. Expired ones stay listed so an admin can resend them. Needs a dashboard session: secret API keys cannot manage members."
      security:
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: Open invites.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Invite"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
              example:
                object: list
                items:
                  - object: invite
                    id: inv_4f8k2m9q1x7z
                    email: sam@example.com
                    role: developer
                    status: pending
                    invited_by: usr_8k2m4q
                    created_at: 1790800914012
                    last_sent_at: 1790800914012
                    expires_at: 1791405714012
                next_page: null
                url: /v2/projects/proj18pzzkao/invites
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/members.ts
      x-scopes:
        - project_configuration:collaborators:read
      x-revenuedot-extension: true
    post:
      operationId: createInvite
      tags:
        - Members and invites
      summary: Invite someone by email
      description: |-
        Admins only. Emails a link that lasts 7 days. Inviting an address that already has an open invite replaces it: the role changes, a new link goes out and the old one stops working. See [Invite your team](../docs/guides/team.md).

        On RevenueDot Cloud the admin needs a confirmed email address. A project can send 50 invites (including resends) per day; after that the answer is 429. `email_sent` is false when the mail server refused the email; the invite still exists and can be resent.
      security:
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                  maxLength: 320
                role:
                  type: string
                  enum:
                    - admin
                    - developer
                    - viewer
              required:
                - email
                - role
            example:
              email: sam@example.com
              role: developer
      responses:
        "201":
          description: The invite.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Invite"
                  - type: object
                    properties:
                      email_sent:
                        type: boolean
                        description: Whether the mail server accepted the email.
              example:
                object: invite
                id: inv_4f8k2m9q1x7z
                email: sam@example.com
                role: developer
                status: pending
                invited_by: usr_8k2m4q
                created_at: 1790800914012
                last_sent_at: 1790800914012
                expires_at: 1791405714012
                email_sent: true
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "409":
          $ref: "#/components/responses/V2Error409"
        "429":
          $ref: "#/components/responses/V2Error429"
      x-source: routes/v2/members.ts
      x-revenuedot-extension: true
  /v2/projects/{project_id}/invites/{invite_id}/actions/resend:
    post:
      operationId: resendInvite
      tags:
        - Members and invites
      summary: Resend an invite
      description: Admins only. Sends a new link valid for 7 more days; the old link stops working. Works on expired invites. Counts toward the 50 invites per project per day.
      security:
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: invite_id
          in: path
          required: true
          schema:
            type: string
          description: Invite id (inv_...).
      responses:
        "200":
          description: The invite.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Invite"
                  - type: object
                    properties:
                      email_sent:
                        type: boolean
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "429":
          $ref: "#/components/responses/V2Error429"
      x-source: routes/v2/members.ts
      x-revenuedot-extension: true
  /v2/projects/{project_id}/invites/{invite_id}:
    delete:
      operationId: revokeInvite
      tags:
        - Members and invites
      summary: Revoke an invite
      description: Admins only. The link stops working at once.
      security:
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: invite_id
          in: path
          required: true
          schema:
            type: string
          description: Invite id (inv_...).
      responses:
        "200":
          description: Deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deleted"
              example:
                object: invite
                id: …
                deleted_at: 1790801342625
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/members.ts
      x-revenuedot-extension: true
  /v2/projects/{project_id}/collaborators/{user_id}:
    post:
      operationId: updateCollaborator
      tags:
        - Members and invites
      summary: Change a member's role
      description: "Admins only. A project always keeps at least one admin, so the last admin cannot be demoted (400). The response uses RevenueCat's role names: `viewer` comes back as `read_only`."
      security:
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: user_id
          in: path
          required: true
          schema:
            type: string
          description: The member's user id (the collaborator `id`).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                role:
                  type: string
                  enum:
                    - admin
                    - developer
                    - viewer
              required:
                - role
            example:
              role: viewer
      responses:
        "200":
          description: The member.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Collaborator"
              example:
                object: collaborator
                id: usr_3n7p1x
                name: Sam
                email: sam@example.com
                role: read_only
                accepted_at: 1790800914012
                has_mfa: false
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/members.ts
      x-revenuedot-extension: true
    delete:
      operationId: removeCollaborator
      tags:
        - Members and invites
      summary: Remove a member, or leave the project
      description: "Any member can remove themselves. Removing someone else takes an admin. The last admin cannot leave or be removed (422): make someone else an admin first, or delete the project."
      security:
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: user_id
          in: path
          required: true
          schema:
            type: string
          description: The member's user id. Your own id leaves the project.
      responses:
        "200":
          description: Deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deleted"
              example:
                object: collaborator
                id: …
                deleted_at: 1790801342625
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "422":
          $ref: "#/components/responses/V2Error422"
      x-source: routes/v2/members.ts
      x-revenuedot-extension: true
  /v2/projects/{project_id}/apps/{app_id}/store_settings:
    get:
      operationId: getStoreSettings
      tags:
        - Store setup
      summary: Store setup state of an app
      description: The notification URL to paste into App Store Connect or Pub/Sub, the notification status, the forwarding URL and which credentials are set. Never a secret.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/AppId"
      responses:
        "200":
          description: The settings.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StoreSettings"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/setup.ts
      x-scopes:
        - project_configuration:apps:read
      x-revenuedot-extension: true
  /v2/projects/{project_id}/apps/{app_id}/actions/verify_credentials:
    post:
      operationId: verifyCredentials
      tags:
        - Store setup
      summary: Check store credentials with Apple or Google
      description: Makes one harmless call to the App Store Server API or the Play Developer API. Values in the body are checked before you save them; missing values fall back to the saved ones.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/AppId"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                app_store:
                  type: object
                  properties:
                    bundle_id:
                      type:
                        - string
                        - "null"
                    subscription_private_key:
                      type:
                        - string
                        - "null"
                    subscription_key_id:
                      type:
                        - string
                        - "null"
                    subscription_key_issuer:
                      type:
                        - string
                        - "null"
                mac_app_store:
                  type: object
                  properties:
                    bundle_id:
                      type:
                        - string
                        - "null"
                    subscription_private_key:
                      type:
                        - string
                        - "null"
                    subscription_key_id:
                      type:
                        - string
                        - "null"
                    subscription_key_issuer:
                      type:
                        - string
                        - "null"
                play_store:
                  type: object
                  properties:
                    package_name:
                      type:
                        - string
                        - "null"
                    play_service_account_credentials_json:
                      oneOf:
                        - type: string
                        - type: object
                        - type: "null"
            example: {}
      responses:
        "200":
          description: The result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CredentialsCheck"
              example:
                object: credentials_check
                app_id: appugfw01uy
                store: app_store
                status: invalid
                valid: false
                message: No in-app purchase key yet. Add the .p8 file, the key ID and the issuer ID.
                checked_at: 1790801342700
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/setup.ts
      x-scopes:
        - project_configuration:apps:read
      x-revenuedot-extension: true
  /v2/projects/{project_id}/apps/{app_id}/actions/mass_extend:
    post:
      operationId: massExtend
      tags:
        - Store setup
      summary: Extend every active App Store subscriber of a product
      description: Asks Apple to extend renewal dates for all active subscribers of `product_id`. Apple then sends one notification per subscription, which records SUBSCRIPTION_EXTENDED.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/AppId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                product_id:
                  type: string
                extend_by_days:
                  type: integer
                  minimum: 1
                  maximum: 90
                extend_reason_code:
                  type: string
                  enum:
                    - undeclared
                    - customer_satisfaction
                    - other
                    - service_issue_or_outage
                storefront_country_codes:
                  type: array
                  items:
                    type: string
                    description: ISO 3166-1 alpha-3, for example USA.
                environment:
                  type: string
                  enum:
                    - production
                    - sandbox
              required:
                - product_id
                - extend_by_days
                - extend_reason_code
            example:
              product_id: pro_monthly
              extend_by_days: 3
              extend_reason_code: service_issue_or_outage
      responses:
        "202":
          description: Accepted by Apple.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MassExtension"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "422":
          $ref: "#/components/responses/V2Error422"
        "503":
          $ref: "#/components/responses/V2Error503"
      x-source: routes/v2/setup.ts
      x-scopes:
        - customer_information:subscriptions:read_write
      x-revenuedot-extension: true
  /v2/projects/{project_id}/apps/{app_id}/mass_extensions/{request_id}:
    get:
      operationId: getMassExtension
      tags:
        - Store setup
      summary: Status of a mass extension
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/AppId"
        - name: request_id
          in: path
          required: true
          schema:
            type: string
          description: The `id` from the mass extend answer.
        - name: product_id
          in: query
          required: true
          schema:
            type: string
        - name: environment
          in: query
          schema:
            type: string
            enum:
              - production
              - sandbox
      responses:
        "200":
          description: The status.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MassExtension"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "422":
          $ref: "#/components/responses/V2Error422"
        "503":
          $ref: "#/components/responses/V2Error503"
      x-source: routes/v2/setup.ts
      x-scopes:
        - customer_information:subscriptions:read
      x-revenuedot-extension: true
  /v2/projects/{project_id}/integrations/webhooks/{webhook_integration_id}/test:
    post:
      operationId: testWebhook
      tags:
        - Webhook deliveries
      summary: Send a TEST event to one webhook
      description: Queues a purchase-shaped TEST event, signed and retried like any delivery. The webhook's filters do not apply. A paused webhook (`enabled` false) answers 422.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: webhook_integration_id
          in: path
          required: true
          schema:
            type: string
          description: Webhook id.
      responses:
        "201":
          description: The queued delivery.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDelivery"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "422":
          $ref: "#/components/responses/V2Error422"
      x-source: routes/v2/setup.ts
      x-scopes:
        - project_configuration:integrations:read_write
      x-revenuedot-extension: true
  /v2/projects/{project_id}/webhooks:
    get:
      operationId: listWebhookStates
      tags:
        - Webhook deliveries
      summary: Whether each webhook is enabled
      description: RevenueCat's webhook object has no `enabled` field, so it is read here. Set it with `POST .../integrations/webhooks/{id}`.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/WebhookState"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/extensions.ts
      x-scopes:
        - project_configuration:integrations:read
      x-revenuedot-extension: true
  /v2/projects/{project_id}/webhooks/{webhook_id}/deliveries:
    get:
      operationId: listWebhookDeliveries
      tags:
        - Webhook deliveries
      summary: Delivery log of a webhook
      description: Newest first.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: webhook_id
          in: path
          required: true
          schema:
            type: string
          description: Webhook id (wh_...).
        - name: status
          in: query
          schema:
            type: string
            enum:
              - pending
              - delivered
              - failed
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/WebhookDelivery"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/extensions.ts
      x-scopes:
        - project_configuration:integrations:read
      x-revenuedot-extension: true
  /v2/projects/{project_id}/webhooks/{webhook_id}/deliveries/{delivery_id}/retry:
    post:
      operationId: retryWebhookDelivery
      tags:
        - Webhook deliveries
      summary: Retry a delivery now
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: webhook_id
          in: path
          required: true
          schema:
            type: string
          description: Webhook id.
        - name: delivery_id
          in: path
          required: true
          schema:
            type: string
          description: Delivery id.
      responses:
        "200":
          description: The delivery, queued.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDelivery"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/extensions.ts
      x-scopes:
        - project_configuration:integrations:read_write
      x-revenuedot-extension: true
  /v2/projects/{project_id}/events:
    get:
      operationId: listEvents
      tags:
        - Event log
      summary: Event log
      description: Every event the project recorded, newest first. `body` is exactly what webhooks receive.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: type
          in: query
          schema:
            type: array
            items:
              type: string
          style: form
          explode: true
          description: Event types (any case); repeat or comma-separate.
        - name: customer
          in: query
          schema:
            type: string
          description: Any app user id of the customer.
        - $ref: "#/components/parameters/Environment"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Event"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/extensions.ts
      x-scopes:
        - customer_information:customers:read
      x-revenuedot-extension: true
  /v2/projects/{project_id}/transactions:
    get:
      operationId: listTransactions
      tags:
        - Event log
      summary: Transaction feed
      description: Every purchase, renewal, trial start, refund and refund reversal, newest first.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: customer
          in: query
          schema:
            type: string
          description: Any app user id of the customer.
        - $ref: "#/components/parameters/Environment"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Transaction"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/extensions.ts
      x-scopes:
        - customer_information:purchases:read
      x-revenuedot-extension: true
  /v2/projects/{project_id}/setup_health:
    get:
      operationId: getSetupHealth
      tags:
        - Store setup
      summary: Setup health
      description: "Per app: the notification URL and whether notifications arrive. For webhooks: deliveries in the last 24 hours and failing endpoints. Also the SDK versions calling the server."
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: Setup health.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SetupHealth"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/extensions.ts
      x-scopes:
        - project_configuration:apps:read
      x-revenuedot-extension: true
  /v2/projects/{project_id}/api_keys:
    get:
      operationId: listApiKeys
      tags:
        - API keys
      summary: List secret keys
      description: Never returns the key itself.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/ApiKey"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/extensions.ts
      x-scopes:
        - project_configuration:api_keys:read
      x-revenuedot-extension: true
    post:
      operationId: createApiKey
      tags:
        - API keys
      summary: Create a secret key
      description: The answer includes `key` once. `permissions` default to `["*"]`. A key cannot create a key with permissions it does not hold.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  maxLength: 100
                permissions:
                  type: array
                  items:
                    type: string
                  minItems: 1
                  maxItems: 100
              required:
                - name
            example:
              name: Backend (read only)
              permissions:
                - customer_information:customers:read
      responses:
        "201":
          description: The key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiKey"
              example:
                object: api_key
                id: key_08ec817fce
                name: Backend (read only)
                prefix: sk_08ec
                permissions:
                  - customer_information:customers:read
                created_at: 1790801342634
                last_used_at: null
                key: sk_08ec817fceead27005772bb943c2bd225850eb931c9835f0
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/extensions.ts
      x-scopes:
        - project_configuration:api_keys:read_write
      x-revenuedot-extension: true
  /v2/projects/{project_id}/api_keys/{key_id}:
    delete:
      operationId: deleteApiKey
      tags:
        - API keys
      summary: Delete a secret key
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: key_id
          in: path
          required: true
          schema:
            type: string
          description: Key id (key_...).
      responses:
        "200":
          description: Deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deleted"
              example:
                object: api_key
                id: …
                deleted_at: 1790801342625
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/extensions.ts
      x-scopes:
        - project_configuration:api_keys:read_write
      x-revenuedot-extension: true
  /v2/projects/{project_id}/test_purchases:
    post:
      operationId: createTestPurchase
      tags:
        - Test Store
      summary: Simulate a Test Store purchase or lifecycle
      description: |-
        Runs a purchase through the same pipeline as an SDK receipt, so events, the transaction ledger and webhooks come out as they would. Scenarios:
        `purchase`, `trial`, `trial_conversion`, `renewal`, `cancel`, `billing_issue`, `refund`, `expire`. See [the Test Store guide](../docs/guides/test-store.md).
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                app_user_id:
                  type: string
                  maxLength: 100
                product_id:
                  type: string
                  description: Product id or store identifier of a Test Store product.
                app_id:
                  type: string
                  description: Test Store app; default the project's first.
                price:
                  type: number
                  minimum: 0
                currency:
                  type: string
                  description: Three letters; default USD.
                purchased_at:
                  type: integer
                  description: Start, epoch milliseconds. Not with offset_days.
                presented_offering_id:
                  type: string
                scenario:
                  type: string
                  enum:
                    - purchase
                    - trial
                    - trial_conversion
                    - renewal
                    - cancel
                    - billing_issue
                    - refund
                    - expire
                offset_days:
                  type: number
                  description: Days ago the scenario starts (0 to 730).
                  minimum: 0
                  maximum: 730
                country_code:
                  type: string
                  description: ISO 3166-1 alpha-2, upper case.
              required:
                - app_user_id
                - product_id
            example:
              app_user_id: user_renewal
              product_id: pro_monthly
              scenario: renewal
              price: 9.99
      responses:
        "201":
          description: What happened.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TestPurchase"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "422":
          $ref: "#/components/responses/V2Error422"
      x-source: routes/v2/extensions.ts
      x-scopes:
        - customer_information:purchases:read_write
      x-revenuedot-extension: true
  /v2/projects/{project_id}/metrics/history:
    get:
      operationId: getMetricHistory
      tags:
        - Dashboard data
      summary: Daily history of an overview metric
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: metric
          in: query
          required: true
          schema:
            type: string
            enum:
              - active_trials
              - active_subscriptions
              - mrr
              - revenue
              - new_customers
              - active_users
        - name: days
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 366
            default: 28
        - name: environment
          in: query
          schema:
            type: string
            enum:
              - production
              - sandbox
      responses:
        "200":
          description: The history.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MetricHistory"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/extensions.ts
      x-scopes:
        - charts_metrics:overview:read
      x-revenuedot-extension: true
  /v2/projects/{project_id}/customer_summaries:
    get:
      operationId: listCustomerSummaries
      tags:
        - Dashboard data
      summary: Dashboard rows for customers
      description: Revenue, entitlement names and prices per customer. Unknown ids are left out.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: ids
          in: query
          required: true
          schema:
            type: string
          description: Up to 100 app user ids, comma separated or repeated.
      responses:
        "200":
          description: A page of results.
          content:
            application/json:
              schema:
                type: object
                required:
                  - object
                  - items
                  - next_page
                  - url
                properties:
                  object:
                    type: string
                    const: list
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/CustomerSummary"
                  next_page:
                    type:
                      - string
                      - "null"
                    description: Path of the next page, or null on the last page.
                  url:
                    type: string
                    description: Path of this list.
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/extensions.ts
      x-scopes:
        - customer_information:customers:read
      x-revenuedot-extension: true
  /v2/projects/{project_id}/import/customers:
    post:
      operationId: importCustomers
      tags:
        - Migration import
      summary: Import customers with their purchases
      description: |-
        Up to 100 RevenueCat-shaped customers per call, each with aliases, attributes, subscriptions and one-time purchases. Writes state directly: no events and no webhooks unless `emit_events` is true.
        Keeps first-seen dates, original purchase dates and store transaction ids, and keys each subscription like the store adapters do (Apple original transaction id, Google purchase token), so later receipts and notifications update the imported row. Running the same import twice changes nothing.
        Google subscriptions without `purchase_token` are keyed `needs_token_refresh:<order id>` until a token is found. The `revenuedot import` CLI calls this; see [the importer](../docs/migrate/importer.md).
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                customers:
                  type: array
                  items:
                    type: object
                    properties:
                      id:
                        type: string
                      aliases:
                        type: array
                        items:
                          type: string
                      first_seen_at:
                        type: integer
                      last_seen_at:
                        type: integer
                      last_seen_app_version:
                        type:
                          - string
                          - "null"
                      last_seen_country:
                        type:
                          - string
                          - "null"
                      last_seen_platform:
                        type:
                          - string
                          - "null"
                      attributes:
                        type: array
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                            value:
                              type:
                                - string
                                - "null"
                            updated_at:
                              type: integer
                          required:
                            - name
                            - value
                      subscriptions:
                        type: array
                        items:
                          type: object
                          properties:
                            source_id:
                              type: string
                            app_id:
                              type:
                                - string
                                - "null"
                            store:
                              type: string
                            product_identifier:
                              type: string
                            environment:
                              type: string
                              enum:
                                - production
                                - sandbox
                            ownership:
                              type: string
                              enum:
                                - purchased
                                - family_shared
                            starts_at:
                              type: integer
                            current_period_starts_at:
                              type: integer
                            current_period_ends_at:
                              type:
                                - integer
                                - "null"
                            status:
                              type: string
                              enum:
                                - trialing
                                - active
                                - expired
                                - in_grace_period
                                - in_billing_retry
                                - paused
                                - unknown
                                - incomplete
                            auto_renewal_status:
                              type: string
                              enum:
                                - will_renew
                                - will_not_renew
                                - will_change_product
                                - will_pause
                                - requires_price_increase_consent
                                - has_already_renewed
                            store_subscription_identifier:
                              type: string
                            original_transaction_id:
                              type:
                                - string
                                - "null"
                            original_transaction_id_confirmed:
                              type: boolean
                            purchase_token:
                              type:
                                - string
                                - "null"
                            period_type:
                              type: string
                              enum:
                                - normal
                                - trial
                                - intro
                                - promotional
                                - prepaid
                            country:
                              type:
                                - string
                                - "null"
                            price:
                              type:
                                - object
                                - "null"
                            total_revenue_usd:
                              type:
                                - number
                                - "null"
                            unsubscribe_detected_at:
                              type:
                                - integer
                                - "null"
                            billing_issues_detected_at:
                              type:
                                - integer
                                - "null"
                            grace_period_expires_at:
                              type:
                                - integer
                                - "null"
                            refunded_at:
                              type:
                                - integer
                                - "null"
                            auto_resume_at:
                              type:
                                - integer
                                - "null"
                            entitlement_lookup_keys:
                              type: array
                              items:
                                type: string
                            auto_renew_product_identifier:
                              type:
                                - string
                                - "null"
                            transactions:
                              type: array
                              items:
                                type: object
                                properties:
                                  id:
                                    type: string
                                  purchased_at:
                                    type: integer
                                  expires_at:
                                    type:
                                      - integer
                                      - "null"
                                  revenue_usd:
                                    type:
                                      - number
                                      - "null"
                                  price:
                                    type:
                                      - object
                                      - "null"
                                required:
                                  - id
                                  - purchased_at
                          required:
                            - store
                            - product_identifier
                            - starts_at
                            - current_period_starts_at
                            - status
                            - store_subscription_identifier
                      purchases:
                        type: array
                        items:
                          type: object
                          properties:
                            source_id:
                              type: string
                            app_id:
                              type:
                                - string
                                - "null"
                            store:
                              type: string
                            product_identifier:
                              type: string
                            environment:
                              type: string
                              enum:
                                - production
                                - sandbox
                            purchased_at:
                              type: integer
                            store_purchase_identifier:
                              type: string
                            status:
                              type: string
                              enum:
                                - owned
                                - refunded
                            refunded_at:
                              type:
                                - integer
                                - "null"
                            consumable:
                              type: boolean
                            price:
                              type:
                                - object
                                - "null"
                            revenue_usd:
                              type:
                                - number
                                - "null"
                            country:
                              type:
                                - string
                                - "null"
                          required:
                            - store
                            - product_identifier
                            - purchased_at
                            - store_purchase_identifier
                    required:
                      - id
                  minItems: 1
                  maxItems: 100
                emit_events:
                  type: boolean
                  description: Default false.
                resolve_store_ids:
                  type: boolean
                  description: "Default true: use the app's store credentials to confirm Apple ids and find Google tokens."
              required:
                - customers
            example:
              customers:
                - id: imported_1
                  aliases:
                    - $RCAnonymousID:0f1e2d
                  first_seen_at: 1735689600000
                  attributes:
                    - name: $email
                      value: ana@example.com
                  subscriptions:
                    - store: test_store
                      app_id: appvnrm0a5h
                      product_identifier: pro_annual
                      starts_at: 1735689600000
                      current_period_starts_at: 1767225600000
                      current_period_ends_at: 1798761600000
                      status: active
                      auto_renewal_status: will_renew
                      store_subscription_identifier: test_1767225600000_imported
      responses:
        "200":
          description: A report per customer.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImportResult"
              example:
                object: import_result
                emit_events: false
                customers:
                  - id: imported_1
                    status: created
                    subscriptions: 1
                    purchases: 0
                    needs_token_refresh: 0
                    notes: []
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/import.ts
      x-scopes:
        - customer_information:customers:read_write
      x-revenuedot-extension: true
  /v2/projects/{project_id}/import/apps/{app_id}/public_key:
    post:
      operationId: importPublicKey
      tags:
        - Migration import
      summary: Keep an app's existing SDK key
      description: Sets the app's public key to the one your shipped app binaries already send (appl_..., goog_...), so old app versions work against RevenueDot. The prefix must match the app's store.
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/AppId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                public_key:
                  type: string
                  minLength: 4
                  maxLength: 255
              required:
                - public_key
            example:
              public_key: appl_AbCdEfGhIjKlMnOpQrStUvWxYz
      responses:
        "200":
          description: The key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicApiKey"
        "400":
          $ref: "#/components/responses/V2Error400"
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
        "409":
          $ref: "#/components/responses/V2Error409"
      x-source: routes/v2/import.ts
      x-scopes:
        - project_configuration:apps:read_write
      x-revenuedot-extension: true
  /v2/projects/{project_id}/import/status:
    get:
      operationId: getImportStatus
      tags:
        - Migration import
      summary: What still needs attention after an import
      security:
        - secretApiKey: []
        - dashboardSession: []
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: Counts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImportStatus"
              example:
                object: import_status
                customers: 13
                subscriptions: 10
                needs_token_refresh: 0
                needs_token_refresh_by_app: {}
        "401":
          $ref: "#/components/responses/V2Error401"
        "403":
          $ref: "#/components/responses/V2Error403"
        "404":
          $ref: "#/components/responses/V2Error404"
      x-source: routes/v2/import.ts
      x-scopes:
        - customer_information:customers:read
      x-revenuedot-extension: true
webhooks:
  INITIAL_PURCHASE:
    post:
      operationId: webhookInitialPurchase
      tags:
        - Webhook events
      summary: INITIAL_PURCHASE
      description: The first purchase of a subscription, including a free trial start.
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    period_type:
                      type: string
                      enum:
                        - NORMAL
                        - TRIAL
                        - INTRO
                        - PROMOTIONAL
                        - PREPAID
                    purchased_at_ms:
                      type: integer
                      description: Start of the period. Epoch milliseconds.
                    expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the period, or null for lifetime. Epoch milliseconds.
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    entitlement_id:
                      type: "null"
                      description: Always null (deprecated in RevenueCat's payload).
                    entitlement_ids:
                      type:
                        - array
                        - "null"
                      items:
                        type: string
                      description: Lookup keys of the entitlements the product unlocks, or null.
                    presented_offering_id:
                      type:
                        - string
                        - "null"
                      description: Offering the purchase was made from, when the SDK sent it.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    is_family_share:
                      type: boolean
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    price:
                      type:
                        - number
                        - "null"
                      description: USD. Money moved only on INITIAL_PURCHASE, RENEWAL, NON_RENEWING_PURCHASE, REFUND_REVERSED and refunds (negative); 0 on other events.
                    price_in_purchased_currency:
                      type:
                        - number
                        - "null"
                      description: Same as price, in `currency`.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    takehome_percentage:
                      type: number
                      description: 1 minus the estimated store commission.
                    tax_percentage:
                      type: number
                      description: Always 0 today.
                    commission_percentage:
                      type: number
                      description: Estimated store commission (0.3 for App Store and Google Play, 0 for Test Store).
                    offer_code:
                      type: "null"
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - original_app_user_id
                    - aliases
                    - product_id
                    - period_type
                    - purchased_at_ms
                    - environment
                    - store
            example:
              api_version: "1.0"
              event:
                id: 66339910-3BFF-49F4-B873-D1283D673DE2
                type: INITIAL_PURCHASE
                event_timestamp_ms: 1790800914034
                app_id: appvnrm0a5h
                app_user_id: user_1
                original_app_user_id: user_1
                aliases:
                  - user_1
                product_id: pro_monthly
                period_type: NORMAL
                purchased_at_ms: 1790800914000
                expiration_at_ms: 1793392914000
                environment: SANDBOX
                entitlement_id: null
                entitlement_ids:
                  - pro
                presented_offering_id: default
                transaction_id: test_1790800914000_quickstart
                original_transaction_id: test_1790800914000_quickstart
                is_family_share: false
                country_code: null
                currency: USD
                price: 9.99
                price_in_purchased_currency: 9.99
                subscriber_attributes: {}
                store: TEST_STORE
                takehome_percentage: 1
                tax_percentage: 0
                commission_percentage: 0
                offer_code: null
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  RENEWAL:
    post:
      operationId: webhookRenewal
      tags:
        - Webhook events
      summary: RENEWAL
      description: "A new paid period: a renewal, a trial converting (`is_trial_conversion: true`), a lapsed customer resubscribing, or a recovered billing issue."
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    period_type:
                      type: string
                      enum:
                        - NORMAL
                        - TRIAL
                        - INTRO
                        - PROMOTIONAL
                        - PREPAID
                    purchased_at_ms:
                      type: integer
                      description: Start of the period. Epoch milliseconds.
                    expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the period, or null for lifetime. Epoch milliseconds.
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    entitlement_id:
                      type: "null"
                      description: Always null (deprecated in RevenueCat's payload).
                    entitlement_ids:
                      type:
                        - array
                        - "null"
                      items:
                        type: string
                      description: Lookup keys of the entitlements the product unlocks, or null.
                    presented_offering_id:
                      type:
                        - string
                        - "null"
                      description: Offering the purchase was made from, when the SDK sent it.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    is_family_share:
                      type: boolean
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    price:
                      type:
                        - number
                        - "null"
                      description: USD. Money moved only on INITIAL_PURCHASE, RENEWAL, NON_RENEWING_PURCHASE, REFUND_REVERSED and refunds (negative); 0 on other events.
                    price_in_purchased_currency:
                      type:
                        - number
                        - "null"
                      description: Same as price, in `currency`.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    takehome_percentage:
                      type: number
                      description: 1 minus the estimated store commission.
                    tax_percentage:
                      type: number
                      description: Always 0 today.
                    commission_percentage:
                      type: number
                      description: Estimated store commission (0.3 for App Store and Google Play, 0 for Test Store).
                    offer_code:
                      type: "null"
                    is_trial_conversion:
                      type: boolean
                      description: True for the first paid period after a free trial.
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - original_app_user_id
                    - aliases
                    - product_id
                    - period_type
                    - purchased_at_ms
                    - environment
                    - store
            example:
              api_version: "1.0"
              event:
                id: 96E753E0-4DEC-4F9B-ABC5-55FBC363769A
                type: RENEWAL
                event_timestamp_ms: 1790800923895
                app_id: appvnrm0a5h
                app_user_id: user_trial_conversion
                original_app_user_id: user_trial_conversion
                aliases:
                  - user_trial_conversion
                product_id: pro_monthly
                period_type: NORMAL
                purchased_at_ms: 1790800923895
                expiration_at_ms: 1793392923895
                environment: SANDBOX
                entitlement_id: null
                entitlement_ids:
                  - pro
                presented_offering_id: null
                transaction_id: test_1790800923895_c8ba6a2d-5f87-4e08-bb4b-f880624bcd6c..1
                original_transaction_id: test_1790800923895_c8ba6a2d-5f87-4e08-bb4b-f880624bcd6c
                is_family_share: false
                country_code: null
                currency: USD
                price: 9.99
                price_in_purchased_currency: 9.99
                subscriber_attributes: {}
                store: TEST_STORE
                takehome_percentage: 1
                tax_percentage: 0
                commission_percentage: 0
                offer_code: null
                is_trial_conversion: true
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  CANCELLATION:
    post:
      operationId: webhookCancellation
      tags:
        - Webhook events
      summary: CANCELLATION
      description: "Auto-renew was turned off, or the purchase was refunded. Access continues to `expiration_at_ms` unless it was a refund. A refund has `cancel_reason: CUSTOMER_SUPPORT` and a negative price."
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    period_type:
                      type: string
                      enum:
                        - NORMAL
                        - TRIAL
                        - INTRO
                        - PROMOTIONAL
                        - PREPAID
                    purchased_at_ms:
                      type: integer
                      description: Start of the period. Epoch milliseconds.
                    expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the period, or null for lifetime. Epoch milliseconds.
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    entitlement_id:
                      type: "null"
                      description: Always null (deprecated in RevenueCat's payload).
                    entitlement_ids:
                      type:
                        - array
                        - "null"
                      items:
                        type: string
                      description: Lookup keys of the entitlements the product unlocks, or null.
                    presented_offering_id:
                      type:
                        - string
                        - "null"
                      description: Offering the purchase was made from, when the SDK sent it.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    is_family_share:
                      type: boolean
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    price:
                      type:
                        - number
                        - "null"
                      description: USD. Money moved only on INITIAL_PURCHASE, RENEWAL, NON_RENEWING_PURCHASE, REFUND_REVERSED and refunds (negative); 0 on other events.
                    price_in_purchased_currency:
                      type:
                        - number
                        - "null"
                      description: Same as price, in `currency`.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    takehome_percentage:
                      type: number
                      description: 1 minus the estimated store commission.
                    tax_percentage:
                      type: number
                      description: Always 0 today.
                    commission_percentage:
                      type: number
                      description: Estimated store commission (0.3 for App Store and Google Play, 0 for Test Store).
                    offer_code:
                      type: "null"
                    cancel_reason:
                      type: string
                      enum:
                        - UNSUBSCRIBE
                        - BILLING_ERROR
                        - DEVELOPER_INITIATED
                        - PRICE_INCREASE
                        - CUSTOMER_SUPPORT
                        - UNKNOWN
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - original_app_user_id
                    - aliases
                    - product_id
                    - period_type
                    - purchased_at_ms
                    - environment
                    - store
            examples:
              unsubscribe:
                summary: Auto-renew turned off
                value:
                  api_version: "1.0"
                  event:
                    id: 5A47B64A-DBC9-48CF-B0BE-F15041559619
                    type: CANCELLATION
                    event_timestamp_ms: 1790800924019
                    app_id: appvnrm0a5h
                    app_user_id: user_cancel
                    original_app_user_id: user_cancel
                    aliases:
                      - user_cancel
                    product_id: pro_monthly
                    period_type: NORMAL
                    purchased_at_ms: 1790800924019
                    expiration_at_ms: 1793392924019
                    environment: SANDBOX
                    entitlement_id: null
                    entitlement_ids:
                      - pro
                    presented_offering_id: null
                    transaction_id: test_1790800924019_bd497110-3c45-48fb-a928-437d22de3e2b
                    original_transaction_id: test_1790800924019_bd497110-3c45-48fb-a928-437d22de3e2b
                    is_family_share: false
                    country_code: null
                    currency: USD
                    price: 0
                    price_in_purchased_currency: 0
                    subscriber_attributes: {}
                    store: TEST_STORE
                    takehome_percentage: 1
                    tax_percentage: 0
                    commission_percentage: 0
                    offer_code: null
                    cancel_reason: UNSUBSCRIBE
              refund:
                summary: Refund
                value:
                  api_version: "1.0"
                  event:
                    id: BF6C2BFB-91A8-4AD8-B3BB-5BE5E708B3DF
                    type: CANCELLATION
                    event_timestamp_ms: 1790800924088
                    app_id: appvnrm0a5h
                    app_user_id: user_refund
                    original_app_user_id: user_refund
                    aliases:
                      - user_refund
                    product_id: pro_monthly
                    period_type: NORMAL
                    purchased_at_ms: 1790800924088
                    expiration_at_ms: 1793392924088
                    environment: SANDBOX
                    entitlement_id: null
                    entitlement_ids:
                      - pro
                    presented_offering_id: null
                    transaction_id: test_1790800924088_d06c5cf7-096e-41e7-8d2c-d91e5ac57fb1
                    original_transaction_id: test_1790800924088_d06c5cf7-096e-41e7-8d2c-d91e5ac57fb1
                    is_family_share: false
                    country_code: null
                    currency: USD
                    price: -9.99
                    price_in_purchased_currency: -9.99
                    subscriber_attributes: {}
                    store: TEST_STORE
                    takehome_percentage: 1
                    tax_percentage: 0
                    commission_percentage: 0
                    offer_code: null
                    cancel_reason: CUSTOMER_SUPPORT
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  UNCANCELLATION:
    post:
      operationId: webhookUncancellation
      tags:
        - Webhook events
      summary: UNCANCELLATION
      description: Auto-renew was turned back on before the subscription expired.
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    period_type:
                      type: string
                      enum:
                        - NORMAL
                        - TRIAL
                        - INTRO
                        - PROMOTIONAL
                        - PREPAID
                    purchased_at_ms:
                      type: integer
                      description: Start of the period. Epoch milliseconds.
                    expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the period, or null for lifetime. Epoch milliseconds.
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    entitlement_id:
                      type: "null"
                      description: Always null (deprecated in RevenueCat's payload).
                    entitlement_ids:
                      type:
                        - array
                        - "null"
                      items:
                        type: string
                      description: Lookup keys of the entitlements the product unlocks, or null.
                    presented_offering_id:
                      type:
                        - string
                        - "null"
                      description: Offering the purchase was made from, when the SDK sent it.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    is_family_share:
                      type: boolean
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    price:
                      type:
                        - number
                        - "null"
                      description: USD. Money moved only on INITIAL_PURCHASE, RENEWAL, NON_RENEWING_PURCHASE, REFUND_REVERSED and refunds (negative); 0 on other events.
                    price_in_purchased_currency:
                      type:
                        - number
                        - "null"
                      description: Same as price, in `currency`.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    takehome_percentage:
                      type: number
                      description: 1 minus the estimated store commission.
                    tax_percentage:
                      type: number
                      description: Always 0 today.
                    commission_percentage:
                      type: number
                      description: Estimated store commission (0.3 for App Store and Google Play, 0 for Test Store).
                    offer_code:
                      type: "null"
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - original_app_user_id
                    - aliases
                    - product_id
                    - period_type
                    - purchased_at_ms
                    - environment
                    - store
            example:
              api_version: "1.0"
              event:
                id: 0C1F6F2E-5B7A-4E8B-9D1C-3A2B1C0D9E01
                type: UNCANCELLATION
                event_timestamp_ms: 1790800914034
                app_id: appugfw01uy
                app_user_id: user_1
                original_app_user_id: user_1
                aliases:
                  - user_1
                product_id: pro_monthly
                period_type: NORMAL
                purchased_at_ms: 1790800914000
                expiration_at_ms: 1793392914000
                environment: PRODUCTION
                entitlement_id: null
                entitlement_ids:
                  - pro
                presented_offering_id: null
                transaction_id: "2000000912345679"
                original_transaction_id: "2000000912345678"
                is_family_share: false
                country_code: US
                currency: USD
                price: 0
                price_in_purchased_currency: 0
                subscriber_attributes: {}
                store: APP_STORE
                takehome_percentage: 0.7
                tax_percentage: 0
                commission_percentage: 0.3
                offer_code: null
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  NON_RENEWING_PURCHASE:
    post:
      operationId: webhookNonRenewingPurchase
      tags:
        - Webhook events
      summary: NON_RENEWING_PURCHASE
      description: "A one-time purchase: consumable, non-consumable or lifetime."
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    period_type:
                      type: string
                      enum:
                        - NORMAL
                        - TRIAL
                        - INTRO
                        - PROMOTIONAL
                        - PREPAID
                    purchased_at_ms:
                      type: integer
                      description: Start of the period. Epoch milliseconds.
                    expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the period, or null for lifetime. Epoch milliseconds.
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    entitlement_id:
                      type: "null"
                      description: Always null (deprecated in RevenueCat's payload).
                    entitlement_ids:
                      type:
                        - array
                        - "null"
                      items:
                        type: string
                      description: Lookup keys of the entitlements the product unlocks, or null.
                    presented_offering_id:
                      type:
                        - string
                        - "null"
                      description: Offering the purchase was made from, when the SDK sent it.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    is_family_share:
                      type: boolean
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    price:
                      type:
                        - number
                        - "null"
                      description: USD. Money moved only on INITIAL_PURCHASE, RENEWAL, NON_RENEWING_PURCHASE, REFUND_REVERSED and refunds (negative); 0 on other events.
                    price_in_purchased_currency:
                      type:
                        - number
                        - "null"
                      description: Same as price, in `currency`.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    takehome_percentage:
                      type: number
                      description: 1 minus the estimated store commission.
                    tax_percentage:
                      type: number
                      description: Always 0 today.
                    commission_percentage:
                      type: number
                      description: Estimated store commission (0.3 for App Store and Google Play, 0 for Test Store).
                    offer_code:
                      type: "null"
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - original_app_user_id
                    - aliases
                    - product_id
                    - period_type
                    - purchased_at_ms
                    - environment
                    - store
            example:
              api_version: "1.0"
              event:
                id: 457C5C58-FF87-4CF3-908A-EB31E34846E1
                type: NON_RENEWING_PURCHASE
                event_timestamp_ms: 1790800924176
                app_id: appvnrm0a5h
                app_user_id: user_life
                original_app_user_id: user_life
                aliases:
                  - user_life
                product_id: pro_lifetime
                period_type: NORMAL
                purchased_at_ms: 1790800924176
                expiration_at_ms: null
                environment: SANDBOX
                entitlement_id: null
                entitlement_ids:
                  - pro
                presented_offering_id: null
                transaction_id: test_1790800924176_a2fda0f6-622f-4e5d-9a5c-003144fb7a2f
                original_transaction_id: test_1790800924176_a2fda0f6-622f-4e5d-9a5c-003144fb7a2f
                is_family_share: false
                country_code: null
                currency: USD
                price: 49.99
                price_in_purchased_currency: 49.99
                subscriber_attributes: {}
                store: TEST_STORE
                takehome_percentage: 1
                tax_percentage: 0
                commission_percentage: 0
                offer_code: null
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  SUBSCRIPTION_PAUSED:
    post:
      operationId: webhookSubscriptionPaused
      tags:
        - Webhook events
      summary: SUBSCRIPTION_PAUSED
      description: A Google Play subscription is scheduled to pause. It will not renew at the end of the period.
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    period_type:
                      type: string
                      enum:
                        - NORMAL
                        - TRIAL
                        - INTRO
                        - PROMOTIONAL
                        - PREPAID
                    purchased_at_ms:
                      type: integer
                      description: Start of the period. Epoch milliseconds.
                    expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the period, or null for lifetime. Epoch milliseconds.
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    entitlement_id:
                      type: "null"
                      description: Always null (deprecated in RevenueCat's payload).
                    entitlement_ids:
                      type:
                        - array
                        - "null"
                      items:
                        type: string
                      description: Lookup keys of the entitlements the product unlocks, or null.
                    presented_offering_id:
                      type:
                        - string
                        - "null"
                      description: Offering the purchase was made from, when the SDK sent it.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    is_family_share:
                      type: boolean
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    price:
                      type:
                        - number
                        - "null"
                      description: USD. Money moved only on INITIAL_PURCHASE, RENEWAL, NON_RENEWING_PURCHASE, REFUND_REVERSED and refunds (negative); 0 on other events.
                    price_in_purchased_currency:
                      type:
                        - number
                        - "null"
                      description: Same as price, in `currency`.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    takehome_percentage:
                      type: number
                      description: 1 minus the estimated store commission.
                    tax_percentage:
                      type: number
                      description: Always 0 today.
                    commission_percentage:
                      type: number
                      description: Estimated store commission (0.3 for App Store and Google Play, 0 for Test Store).
                    offer_code:
                      type: "null"
                    auto_resume_at_ms:
                      type:
                        - integer
                        - "null"
                      description: When it resumes. Epoch milliseconds.
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - original_app_user_id
                    - aliases
                    - product_id
                    - period_type
                    - purchased_at_ms
                    - environment
                    - store
            example:
              api_version: "1.0"
              event:
                id: 0C1F6F2E-5B7A-4E8B-9D1C-3A2B1C0D9E02
                type: SUBSCRIPTION_PAUSED
                event_timestamp_ms: 1790800914034
                app_id: app9l7z3oij
                app_user_id: user_1
                original_app_user_id: user_1
                aliases:
                  - user_1
                product_id: pro
                period_type: NORMAL
                purchased_at_ms: 1790800914000
                expiration_at_ms: 1793392914000
                environment: PRODUCTION
                entitlement_id: null
                entitlement_ids:
                  - pro
                presented_offering_id: null
                transaction_id: GPA.3372-1234-5678-90123..1
                original_transaction_id: GPA.3372-1234-5678-90123
                is_family_share: false
                country_code: US
                currency: USD
                price: 0
                price_in_purchased_currency: 0
                subscriber_attributes: {}
                store: PLAY_STORE
                takehome_percentage: 0.7
                tax_percentage: 0
                commission_percentage: 0.3
                offer_code: null
                auto_resume_at_ms: 1796071314000
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  EXPIRATION:
    post:
      operationId: webhookExpiration
      tags:
        - Webhook events
      summary: EXPIRATION
      description: "Access ended: the period ran out, billing retry gave up or the subscription paused."
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    period_type:
                      type: string
                      enum:
                        - NORMAL
                        - TRIAL
                        - INTRO
                        - PROMOTIONAL
                        - PREPAID
                    purchased_at_ms:
                      type: integer
                      description: Start of the period. Epoch milliseconds.
                    expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the period, or null for lifetime. Epoch milliseconds.
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    entitlement_id:
                      type: "null"
                      description: Always null (deprecated in RevenueCat's payload).
                    entitlement_ids:
                      type:
                        - array
                        - "null"
                      items:
                        type: string
                      description: Lookup keys of the entitlements the product unlocks, or null.
                    presented_offering_id:
                      type:
                        - string
                        - "null"
                      description: Offering the purchase was made from, when the SDK sent it.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    is_family_share:
                      type: boolean
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    price:
                      type:
                        - number
                        - "null"
                      description: USD. Money moved only on INITIAL_PURCHASE, RENEWAL, NON_RENEWING_PURCHASE, REFUND_REVERSED and refunds (negative); 0 on other events.
                    price_in_purchased_currency:
                      type:
                        - number
                        - "null"
                      description: Same as price, in `currency`.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    takehome_percentage:
                      type: number
                      description: 1 minus the estimated store commission.
                    tax_percentage:
                      type: number
                      description: Always 0 today.
                    commission_percentage:
                      type: number
                      description: Estimated store commission (0.3 for App Store and Google Play, 0 for Test Store).
                    offer_code:
                      type: "null"
                    expiration_reason:
                      type: string
                      enum:
                        - UNSUBSCRIBE
                        - BILLING_ERROR
                        - DEVELOPER_INITIATED
                        - PRICE_INCREASE
                        - CUSTOMER_SUPPORT
                        - UNKNOWN
                        - SUBSCRIPTION_PAUSED
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - original_app_user_id
                    - aliases
                    - product_id
                    - period_type
                    - purchased_at_ms
                    - environment
                    - store
            example:
              api_version: "1.0"
              event:
                id: 91A2F69A-FCA7-4C27-9EAE-65C8207BAD7D
                type: EXPIRATION
                event_timestamp_ms: 1790800924128
                app_id: appvnrm0a5h
                app_user_id: user_expire
                original_app_user_id: user_expire
                aliases:
                  - user_expire
                product_id: pro_monthly
                period_type: NORMAL
                purchased_at_ms: 1788122524128
                expiration_at_ms: 1790800924128
                environment: SANDBOX
                entitlement_id: null
                entitlement_ids:
                  - pro
                presented_offering_id: null
                transaction_id: test_1790800924128_a715897b-5461-4c93-abb6-d8d3ea3d11cd
                original_transaction_id: test_1790800924128_a715897b-5461-4c93-abb6-d8d3ea3d11cd
                is_family_share: false
                country_code: null
                currency: USD
                price: 0
                price_in_purchased_currency: 0
                subscriber_attributes: {}
                store: TEST_STORE
                takehome_percentage: 1
                tax_percentage: 0
                commission_percentage: 0
                offer_code: null
                expiration_reason: UNSUBSCRIBE
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  BILLING_ISSUE:
    post:
      operationId: webhookBillingIssue
      tags:
        - Webhook events
      summary: BILLING_ISSUE
      description: A renewal charge failed. The store retries; access may continue in a grace period.
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    period_type:
                      type: string
                      enum:
                        - NORMAL
                        - TRIAL
                        - INTRO
                        - PROMOTIONAL
                        - PREPAID
                    purchased_at_ms:
                      type: integer
                      description: Start of the period. Epoch milliseconds.
                    expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the period, or null for lifetime. Epoch milliseconds.
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    entitlement_id:
                      type: "null"
                      description: Always null (deprecated in RevenueCat's payload).
                    entitlement_ids:
                      type:
                        - array
                        - "null"
                      items:
                        type: string
                      description: Lookup keys of the entitlements the product unlocks, or null.
                    presented_offering_id:
                      type:
                        - string
                        - "null"
                      description: Offering the purchase was made from, when the SDK sent it.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    is_family_share:
                      type: boolean
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    price:
                      type:
                        - number
                        - "null"
                      description: USD. Money moved only on INITIAL_PURCHASE, RENEWAL, NON_RENEWING_PURCHASE, REFUND_REVERSED and refunds (negative); 0 on other events.
                    price_in_purchased_currency:
                      type:
                        - number
                        - "null"
                      description: Same as price, in `currency`.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    takehome_percentage:
                      type: number
                      description: 1 minus the estimated store commission.
                    tax_percentage:
                      type: number
                      description: Always 0 today.
                    commission_percentage:
                      type: number
                      description: Estimated store commission (0.3 for App Store and Google Play, 0 for Test Store).
                    offer_code:
                      type: "null"
                    grace_period_expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the grace period, or null when there is none. Epoch milliseconds.
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - original_app_user_id
                    - aliases
                    - product_id
                    - period_type
                    - purchased_at_ms
                    - environment
                    - store
            example:
              api_version: "1.0"
              event:
                id: 59A0C090-CC89-458B-BDF2-57D9F1447C19
                type: BILLING_ISSUE
                event_timestamp_ms: 1790800924056
                app_id: appvnrm0a5h
                app_user_id: user_billing_issue
                original_app_user_id: user_billing_issue
                aliases:
                  - user_billing_issue
                product_id: pro_monthly
                period_type: NORMAL
                purchased_at_ms: 1788122524056
                expiration_at_ms: 1790800924056
                environment: SANDBOX
                entitlement_id: null
                entitlement_ids:
                  - pro
                presented_offering_id: null
                transaction_id: test_1790800924056_16f6f7bd-6289-454a-b3b8-1fac8424f8a0
                original_transaction_id: test_1790800924056_16f6f7bd-6289-454a-b3b8-1fac8424f8a0
                is_family_share: false
                country_code: null
                currency: USD
                price: 0
                price_in_purchased_currency: 0
                subscriber_attributes: {}
                store: TEST_STORE
                takehome_percentage: 1
                tax_percentage: 0
                commission_percentage: 0
                offer_code: null
                grace_period_expiration_at_ms: 1791405724056
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  PRODUCT_CHANGE:
    post:
      operationId: webhookProductChange
      tags:
        - Webhook events
      summary: PRODUCT_CHANGE
      description: "The customer changed product: an upgrade now, or a downgrade or crossgrade scheduled for the next renewal. `product_id` is the old product."
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    period_type:
                      type: string
                      enum:
                        - NORMAL
                        - TRIAL
                        - INTRO
                        - PROMOTIONAL
                        - PREPAID
                    purchased_at_ms:
                      type: integer
                      description: Start of the period. Epoch milliseconds.
                    expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the period, or null for lifetime. Epoch milliseconds.
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    entitlement_id:
                      type: "null"
                      description: Always null (deprecated in RevenueCat's payload).
                    entitlement_ids:
                      type:
                        - array
                        - "null"
                      items:
                        type: string
                      description: Lookup keys of the entitlements the product unlocks, or null.
                    presented_offering_id:
                      type:
                        - string
                        - "null"
                      description: Offering the purchase was made from, when the SDK sent it.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    is_family_share:
                      type: boolean
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    price:
                      type:
                        - number
                        - "null"
                      description: USD. Money moved only on INITIAL_PURCHASE, RENEWAL, NON_RENEWING_PURCHASE, REFUND_REVERSED and refunds (negative); 0 on other events.
                    price_in_purchased_currency:
                      type:
                        - number
                        - "null"
                      description: Same as price, in `currency`.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    takehome_percentage:
                      type: number
                      description: 1 minus the estimated store commission.
                    tax_percentage:
                      type: number
                      description: Always 0 today.
                    commission_percentage:
                      type: number
                      description: Estimated store commission (0.3 for App Store and Google Play, 0 for Test Store).
                    offer_code:
                      type: "null"
                    new_product_id:
                      type: string
                      description: The product the customer changed to.
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - original_app_user_id
                    - aliases
                    - product_id
                    - period_type
                    - purchased_at_ms
                    - environment
                    - store
            example:
              api_version: "1.0"
              event:
                id: 0C1F6F2E-5B7A-4E8B-9D1C-3A2B1C0D9E03
                type: PRODUCT_CHANGE
                event_timestamp_ms: 1790800914034
                app_id: appugfw01uy
                app_user_id: user_1
                original_app_user_id: user_1
                aliases:
                  - user_1
                product_id: pro_monthly
                new_product_id: pro_annual
                period_type: NORMAL
                purchased_at_ms: 1790800914000
                expiration_at_ms: 1793392914000
                environment: PRODUCTION
                entitlement_id: null
                entitlement_ids:
                  - pro
                presented_offering_id: null
                transaction_id: "2000000912345679"
                original_transaction_id: "2000000912345678"
                is_family_share: false
                country_code: US
                currency: USD
                price: 0
                price_in_purchased_currency: 0
                subscriber_attributes: {}
                store: APP_STORE
                takehome_percentage: 0.7
                tax_percentage: 0
                commission_percentage: 0.3
                offer_code: null
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  SUBSCRIPTION_EXTENDED:
    post:
      operationId: webhookSubscriptionExtended
      tags:
        - Webhook events
      summary: SUBSCRIPTION_EXTENDED
      description: "The current period got longer without a new payment: an App Store renewal extension or a Google Play deferral."
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    period_type:
                      type: string
                      enum:
                        - NORMAL
                        - TRIAL
                        - INTRO
                        - PROMOTIONAL
                        - PREPAID
                    purchased_at_ms:
                      type: integer
                      description: Start of the period. Epoch milliseconds.
                    expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the period, or null for lifetime. Epoch milliseconds.
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    entitlement_id:
                      type: "null"
                      description: Always null (deprecated in RevenueCat's payload).
                    entitlement_ids:
                      type:
                        - array
                        - "null"
                      items:
                        type: string
                      description: Lookup keys of the entitlements the product unlocks, or null.
                    presented_offering_id:
                      type:
                        - string
                        - "null"
                      description: Offering the purchase was made from, when the SDK sent it.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    is_family_share:
                      type: boolean
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    price:
                      type:
                        - number
                        - "null"
                      description: USD. Money moved only on INITIAL_PURCHASE, RENEWAL, NON_RENEWING_PURCHASE, REFUND_REVERSED and refunds (negative); 0 on other events.
                    price_in_purchased_currency:
                      type:
                        - number
                        - "null"
                      description: Same as price, in `currency`.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    takehome_percentage:
                      type: number
                      description: 1 minus the estimated store commission.
                    tax_percentage:
                      type: number
                      description: Always 0 today.
                    commission_percentage:
                      type: number
                      description: Estimated store commission (0.3 for App Store and Google Play, 0 for Test Store).
                    offer_code:
                      type: "null"
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - original_app_user_id
                    - aliases
                    - product_id
                    - period_type
                    - purchased_at_ms
                    - environment
                    - store
            example:
              api_version: "1.0"
              event:
                id: 0C1F6F2E-5B7A-4E8B-9D1C-3A2B1C0D9E04
                type: SUBSCRIPTION_EXTENDED
                event_timestamp_ms: 1790800914034
                app_id: appugfw01uy
                app_user_id: user_1
                original_app_user_id: user_1
                aliases:
                  - user_1
                product_id: pro_monthly
                period_type: NORMAL
                purchased_at_ms: 1790800914000
                expiration_at_ms: 1793997714000
                environment: PRODUCTION
                entitlement_id: null
                entitlement_ids:
                  - pro
                presented_offering_id: null
                transaction_id: "2000000912345679"
                original_transaction_id: "2000000912345678"
                is_family_share: false
                country_code: US
                currency: USD
                price: 0
                price_in_purchased_currency: 0
                subscriber_attributes: {}
                store: APP_STORE
                takehome_percentage: 0.7
                tax_percentage: 0
                commission_percentage: 0.3
                offer_code: null
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  REFUND_REVERSED:
    post:
      operationId: webhookRefundReversed
      tags:
        - Webhook events
      summary: REFUND_REVERSED
      description: A refund was reversed and access is back.
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    period_type:
                      type: string
                      enum:
                        - NORMAL
                        - TRIAL
                        - INTRO
                        - PROMOTIONAL
                        - PREPAID
                    purchased_at_ms:
                      type: integer
                      description: Start of the period. Epoch milliseconds.
                    expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the period, or null for lifetime. Epoch milliseconds.
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    entitlement_id:
                      type: "null"
                      description: Always null (deprecated in RevenueCat's payload).
                    entitlement_ids:
                      type:
                        - array
                        - "null"
                      items:
                        type: string
                      description: Lookup keys of the entitlements the product unlocks, or null.
                    presented_offering_id:
                      type:
                        - string
                        - "null"
                      description: Offering the purchase was made from, when the SDK sent it.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    is_family_share:
                      type: boolean
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    price:
                      type:
                        - number
                        - "null"
                      description: USD. Money moved only on INITIAL_PURCHASE, RENEWAL, NON_RENEWING_PURCHASE, REFUND_REVERSED and refunds (negative); 0 on other events.
                    price_in_purchased_currency:
                      type:
                        - number
                        - "null"
                      description: Same as price, in `currency`.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    takehome_percentage:
                      type: number
                      description: 1 minus the estimated store commission.
                    tax_percentage:
                      type: number
                      description: Always 0 today.
                    commission_percentage:
                      type: number
                      description: Estimated store commission (0.3 for App Store and Google Play, 0 for Test Store).
                    offer_code:
                      type: "null"
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - original_app_user_id
                    - aliases
                    - product_id
                    - period_type
                    - purchased_at_ms
                    - environment
                    - store
            example:
              api_version: "1.0"
              event:
                id: 0C1F6F2E-5B7A-4E8B-9D1C-3A2B1C0D9E05
                type: REFUND_REVERSED
                event_timestamp_ms: 1790800914034
                app_id: appugfw01uy
                app_user_id: user_1
                original_app_user_id: user_1
                aliases:
                  - user_1
                product_id: pro_monthly
                period_type: NORMAL
                purchased_at_ms: 1790800914000
                expiration_at_ms: 1793392914000
                environment: PRODUCTION
                entitlement_id: null
                entitlement_ids:
                  - pro
                presented_offering_id: null
                transaction_id: "2000000912345679"
                original_transaction_id: "2000000912345678"
                is_family_share: false
                country_code: US
                currency: USD
                price: 9.99
                price_in_purchased_currency: 9.99
                subscriber_attributes: {}
                store: APP_STORE
                takehome_percentage: 0.7
                tax_percentage: 0
                commission_percentage: 0.3
                offer_code: null
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  PRICE_INCREASE_CONSENT_REQUIRED:
    post:
      operationId: webhookPriceIncreaseConsentRequired
      tags:
        - Webhook events
      summary: PRICE_INCREASE_CONSENT_REQUIRED
      description: The store asks the customer to accept a price increase.
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - product_id
                    - store
                    - environment
            example:
              api_version: "1.0"
              event:
                id: 0C1F6F2E-5B7A-4E8B-9D1C-3A2B1C0D9E06
                type: PRICE_INCREASE_CONSENT_REQUIRED
                event_timestamp_ms: 1790800914034
                app_id: appugfw01uy
                app_user_id: user_1
                original_app_user_id: user_1
                aliases:
                  - user_1
                product_id: pro_monthly
                environment: PRODUCTION
                transaction_id: "2000000912345679"
                original_transaction_id: "2000000912345678"
                country_code: US
                currency: USD
                subscriber_attributes: {}
                store: APP_STORE
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  PRICE_INCREASE_CONSENT_APPROVED:
    post:
      operationId: webhookPriceIncreaseConsentApproved
      tags:
        - Webhook events
      summary: PRICE_INCREASE_CONSENT_APPROVED
      description: The customer accepted the price increase.
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - product_id
                    - store
                    - environment
            example:
              api_version: "1.0"
              event:
                id: 0C1F6F2E-5B7A-4E8B-9D1C-3A2B1C0D9E07
                type: PRICE_INCREASE_CONSENT_APPROVED
                event_timestamp_ms: 1790800914034
                app_id: appugfw01uy
                app_user_id: user_1
                original_app_user_id: user_1
                aliases:
                  - user_1
                product_id: pro_monthly
                environment: PRODUCTION
                transaction_id: "2000000912345679"
                original_transaction_id: "2000000912345678"
                country_code: US
                currency: USD
                subscriber_attributes: {}
                store: APP_STORE
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  TRANSFER:
    post:
      operationId: webhookTransfer
      tags:
        - Webhook events
      summary: TRANSFER
      description: A purchase moved to another customer because that customer restored it (transfer behaviour `transfer` or `transfer_if_no_active`).
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    transferred_from:
                      type: array
                      items:
                        type: string
                      description: App user ids of the previous owner.
                    transferred_to:
                      type: array
                      items:
                        type: string
                      description: App user ids of the new owner.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - store
                    - environment
                    - transferred_from
                    - transferred_to
            example:
              api_version: "1.0"
              event:
                id: 90CB2D5B-DDD2-4F25-9430-4E5290CC493A
                type: TRANSFER
                event_timestamp_ms: 1790800924235
                app_id: appvnrm0a5h
                environment: SANDBOX
                subscriber_attributes: {}
                store: TEST_STORE
                transferred_from:
                  - alice
                transferred_to:
                  - bob
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
  TEST:
    post:
      operationId: webhookTest
      tags:
        - Webhook events
      summary: TEST
      description: Sent by the dashboard's "Send test event" or `POST .../integrations/webhooks/{id}/test`. Shaped like a purchase.
      parameters:
        - name: X-RevenueCat-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
          description: "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's signing secret>`. Signed again on every attempt."
          example: t=1790800914,v1=0a1552334e825926036f7efe21527800ea45caa63eca523c6120c6da9041ef99
        - name: Authorization
          in: header
          required: false
          schema:
            type: string
          description: The webhook's `authorization_header`, verbatim, when you set one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - api_version
                - event
              properties:
                api_version:
                  type: string
                  const: "1.0"
                event:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique event id (upper-case UUID). Deduplicate on it.
                    type:
                      type: string
                      description: Event type.
                    event_timestamp_ms:
                      type: integer
                      description: When RevenueDot recorded the event. Epoch milliseconds.
                    app_id:
                      type: string
                      description: RevenueDot app id. Left out for promotional grants.
                    app_user_id:
                      type: string
                      description: The app user id the event is about (a non-anonymous alias when there is one).
                    original_app_user_id:
                      type: string
                      description: The customer's first app user id.
                    aliases:
                      type: array
                      items:
                        type: string
                      description: Every app user id of the customer.
                    product_id:
                      type: string
                      description: Store product id. For PRODUCT_CHANGE, the product the customer changed from.
                    period_type:
                      type: string
                      enum:
                        - NORMAL
                        - TRIAL
                        - INTRO
                        - PROMOTIONAL
                        - PREPAID
                    purchased_at_ms:
                      type: integer
                      description: Start of the period. Epoch milliseconds.
                    expiration_at_ms:
                      type:
                        - integer
                        - "null"
                      description: End of the period, or null for lifetime. Epoch milliseconds.
                    environment:
                      type: string
                      enum:
                        - PRODUCTION
                        - SANDBOX
                    entitlement_id:
                      type: "null"
                      description: Always null (deprecated in RevenueCat's payload).
                    entitlement_ids:
                      type:
                        - array
                        - "null"
                      items:
                        type: string
                      description: Lookup keys of the entitlements the product unlocks, or null.
                    presented_offering_id:
                      type:
                        - string
                        - "null"
                      description: Offering the purchase was made from, when the SDK sent it.
                    transaction_id:
                      type:
                        - string
                        - "null"
                      description: Store transaction id of this period.
                    original_transaction_id:
                      type:
                        - string
                        - "null"
                      description: First transaction id of the subscription.
                    is_family_share:
                      type: boolean
                    country_code:
                      type:
                        - string
                        - "null"
                      description: ISO 3166-1 alpha-2.
                    currency:
                      type:
                        - string
                        - "null"
                      description: ISO 4217.
                    price:
                      type:
                        - number
                        - "null"
                      description: USD. Money moved only on INITIAL_PURCHASE, RENEWAL, NON_RENEWING_PURCHASE, REFUND_REVERSED and refunds (negative); 0 on other events.
                    price_in_purchased_currency:
                      type:
                        - number
                        - "null"
                      description: Same as price, in `currency`.
                    subscriber_attributes:
                      type: object
                      additionalProperties:
                        type: object
                        properties:
                          value:
                            type:
                              - string
                              - "null"
                          updated_at_ms:
                            type: integer
                    store:
                      type: string
                      enum:
                        - APP_STORE
                        - MAC_APP_STORE
                        - PLAY_STORE
                        - AMAZON
                        - STRIPE
                        - RC_BILLING
                        - PROMOTIONAL
                        - TEST_STORE
                        - PADDLE
                        - ROKU
                        - EXTERNAL
                    takehome_percentage:
                      type: number
                      description: 1 minus the estimated store commission.
                    tax_percentage:
                      type: number
                      description: Always 0 today.
                    commission_percentage:
                      type: number
                      description: Estimated store commission (0.3 for App Store and Google Play, 0 for Test Store).
                    offer_code:
                      type: "null"
                  required:
                    - id
                    - type
                    - event_timestamp_ms
                    - app_user_id
                    - original_app_user_id
                    - aliases
                    - product_id
                    - period_type
                    - purchased_at_ms
                    - environment
                    - store
            example:
              api_version: "1.0"
              event:
                id: F48A3CEE-2785-4001-9A4D-590F95C1A9F0
                type: TEST
                event_timestamp_ms: 1790800924267
                app_id: appvnrm0a5h
                app_user_id: $RCAnonymousID:10daafcef1da4cea82bdf031565747fc
                original_app_user_id: $RCAnonymousID:10daafcef1da4cea82bdf031565747fc
                aliases:
                  - $RCAnonymousID:10daafcef1da4cea82bdf031565747fc
                product_id: test_product
                period_type: NORMAL
                purchased_at_ms: 1790800924267
                expiration_at_ms: 1793392924267
                environment: SANDBOX
                entitlement_id: null
                entitlement_ids: null
                presented_offering_id: null
                transaction_id: test_transaction_id
                original_transaction_id: test_original_transaction_id
                is_family_share: false
                country_code: US
                currency: USD
                price: 0
                price_in_purchased_currency: 0
                subscriber_attributes: {}
                store: TEST_STORE
                takehome_percentage: 1
                tax_percentage: 0
                commission_percentage: 0
                offer_code: null
      responses:
        "200":
          description: Only HTTP 200 counts as delivered. Anything else, or no answer within 60 seconds, is retried after 5, 10, 20, 40 and 80 minutes.
      x-source: services/events.ts
components:
  schemas:
    V1Error:
      type: object
      properties:
        code:
          type: integer
          description: RevenueCat-compatible backend error code. See the error table.
        message:
          type: string
          description: What went wrong.
        attribute_errors:
          type: array
          items:
            type: object
            properties:
              key_name:
                type: string
              message:
                type: string
          description: Only for 7263.
      required:
        - code
        - message
      x-error-codes:
        - code: 7000
          name: BAD_REQUEST / INVALID_PLATFORM
          status: "400"
          meaning: The request is malformed, a secret-key receipt post has no X-Platform app, the store action does not exist for this store, or a web checkout was asked for (RevenueDot takes no web payments).
        - code: 7101
          name: STORE_PROBLEM
          status: 400 or 503
          meaning: The store refused the request (400), or the store or its credentials could not be used right now (503, retry later).
        - code: 7102
          name: RECEIPT_ALREADY_IN_USE
          status: "400"
          meaning: The purchase belongs to another customer and the project's transfer behaviour is keep or transfer_if_no_active.
        - code: 7103
          name: INVALID_RECEIPT
          status: "400"
          meaning: The receipt, signed transaction or purchase token is not valid, or it belongs to another bundle id or package name.
        - code: 7110
          name: INTERNAL
          status: "500"
          meaning: An unexpected server error. The SDK keeps the transaction and retries.
        - code: 7220
          name: INVALID_APP_USER_ID
          status: "400"
          meaning: The app user id is empty or longer than 100 characters.
        - code: 7224
          name: INVALID_AUTH_TOKEN
          status: "401"
          meaning: A Google Pub/Sub push token is missing or invalid (store notifications only).
        - code: 7225
          name: INVALID_API_KEY
          status: 401 or 403
          meaning: The API key is unknown (401), or a REST v1 endpoint was called with a public key (403).
        - code: 7226
          name: BAD_REQUEST_PARAMS
          status: "400"
          meaning: A store action got parameters it cannot use, or a required field (such as `aad_attribution_token` or `generate_offers`) is missing.
        - code: 7234
          name: INVALID_APPLE_SUBSCRIPTION_KEY
          status: 400 or 500
          meaning: A StoreKit 1 receipt arrived for an App Store app without an in-app purchase key, or the key is incomplete (500, so the SDK retries once you add the key). A promotional offer cannot be signed without the key (400; the SDK reports `invalidAppleSubscriptionKeyError` for that offer).
        - code: 7259
          name: NOT_FOUND
          status: "404"
          meaning: The customer, entitlement, offering or subscription does not exist.
        - code: 7263
          name: INVALID_SUBSCRIBER_ATTRIBUTES
          status: "400"
          meaning: Some attributes were not saved; `attribute_errors` lists them.
        - code: 7662
          name: UNSUPPORTED_RECEIPT
          status: "400"
          meaning: Receipts for this app's store are not supported yet (Amazon, Stripe, Web Billing, Paddle, Roku), including the Android SDK's Amazon receipt lookup.
        - code: 7849
          name: INVALID_WEB_REDEMPTION_TOKEN
          status: "400"
          meaning: A web purchase redemption token is not valid. RevenueDot has no web purchases, so every token answers this; the SDKs return the `invalidToken` result.
        - code: 7877
          name: INVALID_OPERATION_SESSION
          status: "400"
          meaning: A Web Billing checkout session does not exist.
    Price:
      type: object
      properties:
        amount:
          type: number
          description: Price in the purchase currency.
        currency:
          type: string
          description: ISO 4217 currency code.
      required:
        - amount
        - currency
    EntitlementInfo:
      type: object
      properties:
        expires_date:
          type:
            - string
            - "null"
          format: date-time
          description: When access ends. Null for lifetime access. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
        grace_period_expires_date:
          type:
            - string
            - "null"
          format: date-time
          description: End of the billing grace period, when the store grants one. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
        product_identifier:
          type: string
          description: Store product id that gives this access.
        product_plan_identifier:
          type: string
          description: Google Play base plan id, only when there is one.
        purchase_date:
          type: string
          format: date-time
          description: Start of the current period. ISO 8601 in UTC, whole seconds.
      required:
        - expires_date
        - grace_period_expires_date
        - product_identifier
        - purchase_date
    SubscriptionInfo:
      type: object
      properties:
        auto_resume_date:
          type:
            - string
            - "null"
          format: date-time
          description: When a paused Google Play subscription resumes. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
        billing_issues_detected_at:
          type:
            - string
            - "null"
          format: date-time
          description: When the latest renewal failed. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
        display_name:
          type:
            - string
            - "null"
          description: Product display name.
        expires_date:
          type:
            - string
            - "null"
          format: date-time
          description: End of the current period. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
        grace_period_expires_date:
          type:
            - string
            - "null"
          format: date-time
          description: End of the billing grace period. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
        is_sandbox:
          type: boolean
          description: True for sandbox and Test Store purchases.
        management_url:
          type:
            - string
            - "null"
          description: Always null today.
        original_purchase_date:
          type:
            - string
            - "null"
          format: date-time
          description: Start of the subscription. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
        ownership_type:
          type: string
          enum:
            - PURCHASED
            - FAMILY_SHARED
        period_type:
          type: string
          enum:
            - normal
            - trial
            - intro
            - promotional
            - prepaid
        purchase_date:
          type:
            - string
            - "null"
          format: date-time
          description: Start of the current period. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
        refunded_at:
          type:
            - string
            - "null"
          format: date-time
          description: When the store refunded it. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
        store:
          type: string
          enum:
            - app_store
            - mac_app_store
            - play_store
            - amazon
            - stripe
            - rc_billing
            - promotional
            - test_store
            - paddle
            - roku
            - external
        store_transaction_id:
          type:
            - string
            - "null"
          description: Latest store transaction id (Apple), order id (Google) or Test Store token.
        unsubscribe_detected_at:
          type:
            - string
            - "null"
          format: date-time
          description: When auto-renew was turned off. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
        product_plan_identifier:
          type: string
          description: Google Play base plan id, only when there is one.
        price:
          type: object
          properties:
            amount:
              type: number
              description: Price in the purchase currency.
            currency:
              type: string
              description: ISO 4217 currency code.
          required:
            - amount
            - currency
      required:
        - expires_date
        - is_sandbox
        - period_type
        - purchase_date
        - store
    NonSubscriptionInfo:
      type: object
      properties:
        display_name:
          type:
            - string
            - "null"
        id:
          type: string
          description: RevenueDot purchase id.
        is_sandbox:
          type: boolean
        original_purchase_date:
          type:
            - string
            - "null"
          format: date-time
          description: Purchase time. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
        purchase_date:
          type:
            - string
            - "null"
          format: date-time
          description: Purchase time. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
        store:
          type: string
        store_transaction_id:
          type: string
          description: Store transaction id.
        price:
          type: object
          properties:
            amount:
              type: number
              description: Price in the purchase currency.
            currency:
              type: string
              description: ISO 4217 currency code.
          required:
            - amount
            - currency
      required:
        - id
        - is_sandbox
        - purchase_date
        - store
        - store_transaction_id
    CustomerInfo:
      type: object
      properties:
        request_date:
          type: string
          format: date-time
          description: Server time of the response. ISO 8601 in UTC, whole seconds.
        request_date_ms:
          type: integer
          format: int64
          description: Server time of the response. Epoch milliseconds.
        subscriber:
          type: object
          properties:
            entitlements:
              type: object
              additionalProperties:
                $ref: "#/components/schemas/EntitlementInfo"
              description: Entitlements the customer has now or had, keyed by lookup key. Check `expires_date` (or use the SDK's `isActive`).
            first_seen:
              type: string
              format: date-time
              description: When the customer was first seen. ISO 8601 in UTC, whole seconds.
            last_seen:
              type: string
              format: date-time
              description: When the customer was last seen. ISO 8601 in UTC, whole seconds.
            management_url:
              type:
                - string
                - "null"
              description: Always null today.
            non_subscriptions:
              type: object
              additionalProperties:
                type: array
                items:
                  $ref: "#/components/schemas/NonSubscriptionInfo"
              description: One-time purchases by product id, oldest first.
            original_app_user_id:
              type: string
              description: The customer's first app user id.
            original_application_version:
              type:
                - string
                - "null"
              description: Always null today.
            original_purchase_date:
              type:
                - string
                - "null"
              format: date-time
              description: Earliest purchase. ISO 8601 in UTC, whole seconds (for example 2026-10-30T20:41:54Z), or null.
            other_purchases:
              type: object
              description: Always empty.
            subscriber_attributes:
              type: object
              additionalProperties:
                type: object
                properties:
                  value:
                    type:
                      - string
                      - "null"
                  updated_at_ms:
                    type: integer
              description: Only in answers to secret-key requests.
            subscriptions:
              type: object
              additionalProperties:
                $ref: "#/components/schemas/SubscriptionInfo"
              description: Latest subscription per product id.
          required:
            - entitlements
            - first_seen
            - last_seen
            - non_subscriptions
            - original_app_user_id
            - subscriptions
      required:
        - request_date
        - request_date_ms
        - subscriber
    ReceiptResponse:
      allOf:
        - $ref: "#/components/schemas/CustomerInfo"
        - type: object
          properties:
            purchased_products:
              type: object
              additionalProperties:
                type: object
                properties:
                  should_consume:
                    type: boolean
                    description: "True for consumables: the Android SDK consumes the purchase."
              description: One entry per purchase the receipt contained.
    Offerings:
      type: object
      properties:
        current_offering_id:
          type:
            - string
            - "null"
          description: Lookup key of the current offering, or the customer's override.
        offerings:
          type: array
          items:
            type: object
            properties:
              description:
                type: string
                description: Offering display name.
              identifier:
                type: string
                description: Offering lookup key.
              metadata:
                type:
                  - object
                  - "null"
                description: Your JSON metadata.
              packages:
                type: array
                items:
                  type: object
                  properties:
                    identifier:
                      type: string
                      description: Package lookup key, for example $rc_monthly.
                    platform_product_identifier:
                      type: string
                      description: Store product id for the calling app.
                    platform_product_plan_identifier:
                      type: string
                      description: Google Play base plan id, when the product is `subscription:base-plan`.
                  required:
                    - identifier
                    - platform_product_identifier
            required:
              - description
              - identifier
              - metadata
              - packages
      required:
        - current_offering_id
        - offerings
    V2Error:
      type: object
      properties:
        object:
          type: string
          const: error
        type:
          type: string
          enum:
            - parameter_error
            - resource_already_exists
            - resource_missing
            - idempotency_error
            - rate_limit_error
            - authentication_error
            - authorization_error
            - store_error
            - server_error
            - resource_locked_error
            - unprocessable_entity_error
            - invalid_request
            - entity_references_archived_entities
          x-enum-descriptions:
            parameter_error: A field or query parameter is missing or invalid. `param` names it. Fix the request; do not retry it unchanged.
            resource_already_exists: An object with this id or lookup key already exists (409). Fetch it instead of creating it.
            resource_missing: The object does not exist in this project (404). Another project's ids also answer 404, so ids cannot be probed.
            idempotency_error: Reserved for RevenueCat compatibility. RevenueDot does not send it today.
            rate_limit_error: "Too many requests of one kind: project invites (50 per project per day). The error is `retryable`; try again later. The dashboard's password reset and email verification endpoints answer 429 with the same `type`."
            authentication_error: No API key, an unknown key, or no dashboard session (401).
            authorization_error: The key lacks a permission, a public app key was used, or the action needs a dashboard admin (403).
            store_error: "The App Store or Google Play refused the action (422) or could not be reached (503, `retryable: true`)."
            server_error: "RevenueDot failed (500, `retryable: true`). Retry with backoff."
            resource_locked_error: Reserved for RevenueCat compatibility. RevenueDot does not send it today.
            unprocessable_entity_error: The request is valid but not possible in this state or for this store (422), for example archiving the current offering or refunding an App Store purchase.
            invalid_request: The body is not valid JSON (400), or a package would get two products of one app with overlapping eligibility (409).
            entity_references_archived_entities: The action would make an archived object current (422). Unarchive it first.
        message:
          type: string
          description: What went wrong.
        param:
          type: string
          description: The request field at fault, when there is one.
        doc_url:
          type: string
          description: Link to the error's section of the errors page.
        retryable:
          type: boolean
          description: True when retrying the same request can succeed.
      required:
        - object
        - type
        - message
        - doc_url
        - retryable
    Deleted:
      type: object
      properties:
        object:
          type: string
          description: The deleted object's type.
        id:
          type: string
        deleted_at:
          type: integer
          format: int64
          description: When it was deleted. Epoch milliseconds.
      required:
        - object
        - id
        - deleted_at
    MonetaryAmount:
      type: object
      properties:
        currency:
          type: string
          description: ISO 4217 currency code.
        gross:
          type: number
          description: Gross amount.
        commission:
          type: number
          description: Estimated store commission.
        tax:
          type: number
          description: Tax. Always 0 today.
        proceeds:
          type: number
          description: Gross minus commission.
      required:
        - currency
        - gross
        - commission
        - tax
        - proceeds
    Project:
      type: object
      properties:
        object:
          type: string
          const: project
        id:
          type: string
          description: Project id (proj...).
        name:
          type: string
        created_at:
          type: integer
          format: int64
          description: Creation time. Epoch milliseconds.
        icon_url:
          type:
            - string
            - "null"
          description: Always null.
        icon_url_large:
          type:
            - string
            - "null"
          description: Always null.
      required:
        - object
        - id
        - name
        - created_at
    ProjectSettings:
      allOf:
        - $ref: "#/components/schemas/Project"
        - type: object
          properties:
            transfer_behavior:
              type: string
              enum:
                - transfer
                - transfer_if_no_active
                - keep
                - share
              description: What happens when a purchase already owned by another customer is restored. Default transfer.
            sandbox_transfer_behavior:
              type:
                - string
                - "null"
              enum:
                - transfer
                - transfer_if_no_active
                - keep
                - share
                - null
              description: Override for sandbox purchases; null uses transfer_behavior.
          required:
            - transfer_behavior
            - sandbox_transfer_behavior
    App:
      type: object
      properties:
        object:
          type: string
          const: app
        id:
          type: string
          description: App id (app...).
        name:
          type: string
        created_at:
          type: integer
          format: int64
          description: Creation time. Epoch milliseconds.
        type:
          type: string
          enum:
            - amazon
            - app_store
            - mac_app_store
            - play_store
            - stripe
            - rc_billing
            - roku
            - paddle
            - test_store
        project_id:
          type: string
        custom_url_scheme:
          type: string
          description: Derived from the public key.
        app_store:
          type: object
          properties:
            bundle_id:
              type: string
            app_store_connect_api_key_configured:
              type: boolean
            subscription_key_configured:
              type: boolean
              description: True when the in-app purchase key (.p8, key id, issuer id) is set.
            app_store_connect_vendor_number:
              type:
                - string
                - "null"
        mac_app_store:
          type: object
          properties:
            bundle_id:
              type: string
        play_store:
          type: object
          properties:
            package_name:
              type: string
            play_service_account_credentials_configured:
              type: boolean
        amazon:
          type: object
          properties:
            package_name:
              type: string
        stripe:
          type: object
          properties:
            stripe_account_id:
              type:
                - string
                - "null"
        rc_billing:
          type: object
          properties:
            stripe_account_id:
              type:
                - string
                - "null"
            seller_company_name:
              type: string
            app_name:
              type: string
            support_email:
              type:
                - string
                - "null"
            default_currency:
              type: string
        roku:
          type: object
          properties:
            roku_channel_id:
              type:
                - string
                - "null"
            roku_channel_name:
              type:
                - string
                - "null"
        paddle:
          type: object
          properties:
            paddle_is_sandbox:
              type: boolean
            paddle_api_key:
              type: "null"
      required:
        - object
        - id
        - name
        - created_at
        - type
        - project_id
      description: Only the object for the app's own `type` is present. Store secrets are never returned.
    PublicApiKey:
      type: object
      properties:
        object:
          type: string
          const: public_api_key
        id:
          type: string
        key:
          type: string
          description: The key the SDK sends (appl_, goog_, test_ ...).
        environment:
          type: string
          enum:
            - production
            - sandbox
        app_id:
          type: string
        created_at:
          type: integer
          format: int64
          description: Creation time. Epoch milliseconds.
      required:
        - object
        - id
        - key
        - environment
        - app_id
        - created_at
    Product:
      type: object
      properties:
        object:
          type: string
          const: product
        id:
          type: string
          description: Product id (prod...).
        store_identifier:
          type: string
          description: The store's product id. Google Play subscriptions use `subscriptionId:basePlanId`.
        type:
          type: string
          enum:
            - subscription
            - one_time
            - consumable
            - non_consumable
            - non_renewing_subscription
        state:
          type: string
          enum:
            - active
            - inactive
        subscription:
          type: object
          properties:
            duration:
              type:
                - string
                - "null"
              description: ISO 8601 period such as P1M.
            grace_period_duration:
              type: "null"
            trial_duration:
              type: "null"
        one_time:
          type: object
          properties:
            is_consumable:
              type:
                - boolean
                - "null"
        created_at:
          type: integer
          format: int64
          description: Creation time. Epoch milliseconds.
        app_id:
          type: string
        display_name:
          type:
            - string
            - "null"
        app:
          $ref: "#/components/schemas/App"
        indicative_price:
          oneOf:
            - $ref: "#/components/schemas/IndicativePrice"
            - type: "null"
          description: "With `expand=indicative_price`: the Test Store price, or null."
      required:
        - object
        - id
        - store_identifier
        - type
        - state
        - created_at
        - app_id
        - display_name
    IndicativePrice:
      type: object
      properties:
        object:
          type: string
          const: indicative_price
        currency:
          type: string
          description: ISO 4217 code.
        country:
          type: "null"
        amount_micros:
          type: integer
          description: "Price in micros: 9.99 is 9990000."
      required:
        - object
        - currency
        - country
        - amount_micros
    WebhookState:
      type: object
      properties:
        object:
          type: string
          const: webhook_state
        id:
          type: string
          description: Webhook id (wh_...).
        enabled:
          type: boolean
          description: False while deliveries are paused.
      required:
        - object
        - id
        - enabled
    Entitlement:
      type: object
      properties:
        object:
          type: string
          const: entitlement
        id:
          type: string
          description: Entitlement id (entl...).
        project_id:
          type: string
        lookup_key:
          type: string
          description: What apps check, for example `pro`.
        display_name:
          type: string
        created_at:
          type: integer
          format: int64
          description: Creation time. Epoch milliseconds.
        state:
          type: string
          enum:
            - active
            - inactive
        products:
          type: object
          required:
            - object
            - items
            - next_page
            - url
          properties:
            object:
              type: string
              const: list
            items:
              type: array
              items:
                $ref: "#/components/schemas/Product"
            next_page:
              type:
                - string
                - "null"
              description: Path of the next page, or null on the last page.
            url:
              type: string
              description: Path of this list.
      required:
        - object
        - id
        - project_id
        - lookup_key
        - display_name
        - created_at
        - state
    PackageProduct:
      type: object
      properties:
        product:
          $ref: "#/components/schemas/Product"
        eligibility_criteria:
          type: string
          enum:
            - all
            - google_sdk_lt_6
            - google_sdk_ge_6
      required:
        - product
        - eligibility_criteria
    Package:
      type: object
      properties:
        object:
          type: string
          const: package
        id:
          type: string
          description: Package id (pkge...).
        lookup_key:
          type: string
          description: For example $rc_monthly.
        display_name:
          type: string
        position:
          type: integer
          description: Order in the offering, lowest first.
        created_at:
          type: integer
          format: int64
          description: Creation time. Epoch milliseconds.
        products:
          type: object
          required:
            - object
            - items
            - next_page
            - url
          properties:
            object:
              type: string
              const: list
            items:
              type: array
              items:
                $ref: "#/components/schemas/PackageProduct"
            next_page:
              type:
                - string
                - "null"
              description: Path of the next page, or null on the last page.
            url:
              type: string
              description: Path of this list.
      required:
        - object
        - id
        - lookup_key
        - display_name
        - position
        - created_at
    Offering:
      type: object
      properties:
        object:
          type: string
          const: offering
        id:
          type: string
          description: Offering id (ofrng...).
        lookup_key:
          type: string
        display_name:
          type: string
        is_current:
          type: boolean
          description: Exactly one offering per project is current.
        created_at:
          type: integer
          format: int64
          description: Creation time. Epoch milliseconds.
        project_id:
          type: string
        state:
          type: string
          enum:
            - active
            - inactive
        paywall_id:
          type: "null"
        metadata:
          type:
            - object
            - "null"
        packages:
          type: object
          required:
            - object
            - items
            - next_page
            - url
          properties:
            object:
              type: string
              const: list
            items:
              type: array
              items:
                $ref: "#/components/schemas/Package"
            next_page:
              type:
                - string
                - "null"
              description: Path of the next page, or null on the last page.
            url:
              type: string
              description: Path of this list.
      required:
        - object
        - id
        - lookup_key
        - display_name
        - is_current
        - created_at
        - project_id
        - state
        - metadata
    ActiveEntitlement:
      type: object
      properties:
        object:
          type: string
          const: customer.active_entitlement
        entitlement_id:
          type: string
          description: Entitlement id (entl...), not the lookup key.
        expires_at:
          type:
            - integer
            - "null"
          format: int64
          description: When access ends. Epoch milliseconds, or null.
      required:
        - object
        - entitlement_id
        - expires_at
    CustomerAttribute:
      type: object
      properties:
        object:
          type: string
          const: customer.attribute
        name:
          type: string
        value:
          type: string
        updated_at:
          type: integer
          format: int64
          description: Last update. Epoch milliseconds.
      required:
        - object
        - name
        - value
        - updated_at
    Customer:
      type: object
      properties:
        object:
          type: string
          const: customer
        id:
          type: string
          description: The customer's original app user id.
        project_id:
          type: string
        first_seen_at:
          type: integer
          format: int64
          description: First seen. Epoch milliseconds.
        last_seen_at:
          type:
            - integer
            - "null"
          format: int64
          description: Last seen. Epoch milliseconds, or null.
        last_seen_app_version:
          type:
            - string
            - "null"
        last_seen_country:
          type:
            - string
            - "null"
        last_seen_platform:
          type:
            - string
            - "null"
        last_seen_platform_version:
          type: "null"
        active_entitlements:
          type: object
          required:
            - object
            - items
            - next_page
            - url
          properties:
            object:
              type: string
              const: list
            items:
              type: array
              items:
                $ref: "#/components/schemas/ActiveEntitlement"
            next_page:
              type:
                - string
                - "null"
              description: Path of the next page, or null on the last page.
            url:
              type: string
              description: Path of this list.
        experiment:
          type: "null"
        attributes:
          type: object
          required:
            - object
            - items
            - next_page
            - url
          properties:
            object:
              type: string
              const: list
            items:
              type: array
              items:
                $ref: "#/components/schemas/CustomerAttribute"
            next_page:
              type:
                - string
                - "null"
              description: Path of the next page, or null on the last page.
            url:
              type: string
              description: Path of this list.
      required:
        - object
        - id
        - project_id
        - first_seen_at
        - last_seen_at
      description: "`active_entitlements` and `experiment` are present on single-customer answers; `attributes` only with `expand=attributes`."
    CustomerAlias:
      type: object
      properties:
        object:
          type: string
          const: customer.alias
        id:
          type: string
          description: An app user id of the customer.
        created_at:
          type: integer
          format: int64
          description: When it was linked. Epoch milliseconds.
      required:
        - object
        - id
        - created_at
    CustomerEvent:
      type: object
      properties:
        object:
          type: string
          const: customer.event
        id:
          type: string
        app_id:
          type:
            - string
            - "null"
        type:
          type: string
          description: Webhook event type, for example INITIAL_PURCHASE.
        body:
          type: object
          description: The webhook `event` object.
        created_at:
          type: integer
          format: int64
          description: Recorded. Epoch milliseconds.
        occurred_at:
          type: integer
          format: int64
          description: When it happened. Epoch milliseconds.
      required:
        - object
        - id
        - type
        - body
        - created_at
        - occurred_at
    Subscription:
      type: object
      properties:
        object:
          type: string
          const: subscription
        id:
          type: string
          description: Subscription id (sub_...).
        customer_id:
          type: string
        original_customer_id:
          type: string
        product_id:
          type:
            - string
            - "null"
          description: Product id (prod...), null for promotional grants.
        starts_at:
          type: integer
          format: int64
          description: Start of the subscription. Epoch milliseconds.
        current_period_starts_at:
          type: integer
          format: int64
          description: Start of the current period. Epoch milliseconds.
        current_period_ends_at:
          type:
            - integer
            - "null"
          format: int64
          description: End of the current period. Epoch milliseconds, or null.
        ends_at:
          type:
            - integer
            - "null"
          format: int64
          description: End of access. Epoch milliseconds, or null.
        gives_access:
          type: boolean
        pending_payment:
          type: boolean
        auto_renewal_status:
          type: string
          enum:
            - will_renew
            - will_not_renew
            - will_change_product
            - will_pause
        status:
          type: string
          enum:
            - trialing
            - active
            - in_grace_period
            - in_billing_retry
            - paused
            - expired
        total_revenue_in_usd:
          $ref: "#/components/schemas/MonetaryAmount"
        presented_offering_id:
          type:
            - string
            - "null"
          description: Offering the purchase was made from (its id, or the identifier the SDK sent when no such offering exists).
        entitlements:
          type: object
          required:
            - object
            - items
            - next_page
            - url
          properties:
            object:
              type: string
              const: list
            items:
              type: array
              items:
                $ref: "#/components/schemas/Entitlement"
            next_page:
              type:
                - string
                - "null"
              description: Path of the next page, or null on the last page.
            url:
              type: string
              description: Path of this list.
        environment:
          type: string
          enum:
            - production
            - sandbox
        store:
          type: string
        store_subscription_identifier:
          type: string
          description: Latest store transaction id, order id or token.
        ownership:
          type: string
          enum:
            - purchased
            - family_shared
        country:
          type: string
          description: ISO 3166-1 alpha-2, when known.
        management_url:
          type: "null"
      required:
        - object
        - id
        - customer_id
        - starts_at
        - gives_access
        - status
        - auto_renewal_status
        - environment
        - store
    Purchase:
      type: object
      properties:
        object:
          type: string
          const: purchase
        id:
          type: string
        customer_id:
          type: string
        original_customer_id:
          type: string
        product_id:
          type: string
        purchased_at:
          type: integer
          format: int64
          description: Purchase time. Epoch milliseconds.
        revenue_in_usd:
          $ref: "#/components/schemas/MonetaryAmount"
        quantity:
          type: integer
        status:
          type: string
          enum:
            - owned
            - refunded
        presented_offering_id:
          type:
            - string
            - "null"
          description: Offering the purchase was made from (its id, or the identifier the SDK sent when no such offering exists).
        entitlements:
          type: object
          required:
            - object
            - items
            - next_page
            - url
          properties:
            object:
              type: string
              const: list
            items:
              type: array
              items:
                $ref: "#/components/schemas/Entitlement"
            next_page:
              type:
                - string
                - "null"
              description: Path of the next page, or null on the last page.
            url:
              type: string
              description: Path of this list.
        environment:
          type: string
          enum:
            - production
            - sandbox
        store:
          type: string
        store_purchase_identifier:
          type: string
        ownership:
          type: string
          enum:
            - purchased
        country:
          type: string
      required:
        - object
        - id
        - customer_id
        - product_id
        - purchased_at
        - status
        - environment
        - store
    SubscriptionTransaction:
      type: object
      properties:
        object:
          type: string
          const: subscription_transaction
        id:
          type: string
        purchased_at:
          type: integer
          format: int64
          description: Purchase time. Epoch milliseconds.
        product_store_identifier:
          type: string
        revenue_in_local_currency:
          oneOf:
            - $ref: "#/components/schemas/MonetaryAmount"
            - type: "null"
        revenue_in_usd:
          $ref: "#/components/schemas/MonetaryAmount"
        expiration_date:
          type:
            - integer
            - "null"
          format: int64
          description: End of the period. Epoch milliseconds, or null.
        effective_expiration_date:
          type:
            - integer
            - "null"
          format: int64
          description: When access actually ended (the refund time for a refunded period). Epoch milliseconds, or null.
      required:
        - object
        - id
        - purchased_at
    WebhookIntegration:
      type: object
      properties:
        object:
          type: string
          const: webhook_integration
        id:
          type: string
          description: Webhook id (wh_...).
        project_id:
          type: string
        name:
          type: string
        url:
          type: string
        environment:
          type:
            - string
            - "null"
          enum:
            - production
            - sandbox
            - null
          description: Null sends both.
        event_types:
          type: array
          items:
            type: string
          description: Lower-case event types. Empty sends every type.
        app_id:
          type:
            - string
            - "null"
          description: Only events of this app, or null for all.
        created_at:
          type: integer
          format: int64
          description: Creation time. Epoch milliseconds.
        signing_secret:
          type: string
          description: whsec_... Only in the answer that creates the webhook.
      required:
        - object
        - id
        - project_id
        - name
        - url
        - environment
        - event_types
        - app_id
        - created_at
    Collaborator:
      type: object
      properties:
        object:
          type: string
          const: collaborator
        id:
          type: string
        name:
          type:
            - string
            - "null"
        email:
          type: string
        role:
          type: string
          enum:
            - admin
            - developer
            - read_only
          description: RevenueCat's role names. `read_only` is the dashboard's Viewer role.
        accepted_at:
          type: integer
          format: int64
          description: When the user joined. Epoch milliseconds.
        has_mfa:
          type: boolean
          description: Always false.
      required:
        - object
        - id
        - email
        - role
    Invite:
      type: object
      properties:
        object:
          type: string
          const: invite
        id:
          type: string
          description: inv_...
        email:
          type: string
          description: The invited address, lowercased.
        role:
          type: string
          enum:
            - admin
            - developer
            - viewer
          description: The role the person gets when they accept.
        status:
          type: string
          enum:
            - pending
            - expired
            - accepted
            - revoked
          description: Lists only show `pending` and `expired`. An expired invite can be resent.
        invited_by:
          type:
            - string
            - "null"
          description: User id of the admin who last sent it.
        created_at:
          type: integer
          format: int64
          description: When it was created. Epoch milliseconds.
        last_sent_at:
          type: integer
          format: int64
          description: When the last email went out. Epoch milliseconds.
        expires_at:
          type: integer
          format: int64
          description: "When the link stops working: 7 days after it was last sent. Epoch milliseconds."
      required:
        - object
        - id
        - email
        - role
        - status
        - expires_at
    OverviewMetrics:
      type: object
      properties:
        object:
          type: string
          const: overview_metrics
        currency:
          type: string
          const: USD
        metrics:
          type: array
          items:
            type: object
            properties:
              object:
                type: string
                const: overview_metric
              id:
                type: string
                enum:
                  - active_trials
                  - active_subscriptions
                  - mrr
                  - revenue
                  - new_customers
                  - active_users
              name:
                type: string
              description:
                type: string
              unit:
                type: string
                enum:
                  - "#"
                  - $
              period:
                type: string
                enum:
                  - P0D
                  - P28D
              value:
                type: number
              last_updated_at:
                type: integer
                format: int64
                description: Computed at. Epoch milliseconds.
              last_updated_at_iso8601:
                type: string
      required:
        - object
        - currency
        - metrics
    WebhookDelivery:
      type: object
      properties:
        object:
          type: string
          const: webhook_delivery
        id:
          type: string
        webhook_integration_id:
          type: string
        event_id:
          type: string
        event_type:
          type: string
        status:
          type: string
          enum:
            - pending
            - delivered
            - failed
        attempts:
          type: integer
        next_attempt_at:
          type:
            - integer
            - "null"
          format: int64
          description: Next retry, when pending. Epoch milliseconds, or null.
        response_status:
          type:
            - integer
            - "null"
          description: HTTP status of the last attempt.
        response_ms:
          type:
            - integer
            - "null"
          description: Duration of the last attempt.
        last_error:
          type:
            - string
            - "null"
        created_at:
          type: integer
          format: int64
          description: Queued at. Epoch milliseconds.
      required:
        - object
        - id
        - webhook_integration_id
        - event_id
        - event_type
        - status
        - attempts
    Event:
      type: object
      properties:
        object:
          type: string
          const: event
        id:
          type: string
        type:
          type: string
        environment:
          type: string
          enum:
            - production
            - sandbox
        app_id:
          type:
            - string
            - "null"
        customer_id:
          type:
            - string
            - "null"
          description: Original app user id.
        app_user_id:
          type:
            - string
            - "null"
        occurred_at:
          type: integer
          format: int64
          description: When it happened. Epoch milliseconds.
        created_at:
          type: integer
          format: int64
          description: Recorded. Epoch milliseconds.
        body:
          type: object
          description: The webhook `event` object, exactly as webhooks receive it.
      required:
        - object
        - id
        - type
        - environment
        - occurred_at
        - body
    Transaction:
      type: object
      properties:
        object:
          type: string
          const: transaction
        id:
          type: string
        customer_id:
          type: string
        app_id:
          type:
            - string
            - "null"
        store:
          type: string
        store_transaction_id:
          type:
            - string
            - "null"
        product_identifier:
          type: string
        kind:
          type: string
          enum:
            - purchase
            - renewal
            - trial
            - one_time
            - refund
            - refund_reversal
        environment:
          type: string
          enum:
            - production
            - sandbox
        purchased_at:
          type: integer
          format: int64
          description: When the money moved. Epoch milliseconds.
        expires_at:
          type:
            - integer
            - "null"
          format: int64
          description: End of the period. Epoch milliseconds, or null.
        revenue_in_usd:
          type: number
          description: USD; negative for refunds.
        price:
          oneOf:
            - $ref: "#/components/schemas/Price"
            - type: "null"
        country:
          type:
            - string
            - "null"
      required:
        - object
        - id
        - customer_id
        - store
        - product_identifier
        - kind
        - environment
        - purchased_at
        - revenue_in_usd
    ApiKey:
      type: object
      properties:
        object:
          type: string
          const: api_key
        id:
          type: string
        name:
          type: string
        prefix:
          type: string
          description: First 7 characters of the key.
        permissions:
          type: array
          items:
            type: string
          description: Scopes; `*` is every scope.
        created_at:
          type: integer
          format: int64
          description: Creation time. Epoch milliseconds.
        last_used_at:
          type:
            - integer
            - "null"
          format: int64
          description: Last use, updated at most once a minute. Epoch milliseconds, or null.
        key:
          type: string
          description: The secret key (sk_...). Only in the answer that creates it.
      required:
        - object
        - id
        - name
        - prefix
        - permissions
        - created_at
        - last_used_at
    SetupHealth:
      type: object
      properties:
        object:
          type: string
          const: setup_health
        project_id:
          type: string
        checked_at:
          type: integer
          format: int64
          description: Computed at. Epoch milliseconds.
        apps:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              type:
                type: string
              notification_url:
                type:
                  - string
                  - "null"
                description: Where the store must send notifications.
              last_notification_at:
                type:
                  - integer
                  - "null"
                format: int64
                description: Last notification processed for a known purchase (or the store's test). Epoch milliseconds, or null.
              last_notification_received_at:
                type:
                  - integer
                  - "null"
                format: int64
                description: Last notification received at all. Epoch milliseconds, or null.
              last_notification_error:
                type:
                  - object
                  - "null"
                properties:
                  at:
                    type: integer
                  type:
                    type:
                      - string
                      - "null"
                  message:
                    type: string
              notification_status:
                type: string
                enum:
                  - ready
                  - failing
                  - received
                  - waiting
              credentials_configured:
                type: boolean
        webhooks:
          type: object
          properties:
            total:
              type: integer
            attempted_24h:
              type: integer
            delivered_24h:
              type: integer
            failed_24h:
              type: integer
            pending:
              type: integer
            delivered_percent_24h:
              type:
                - number
                - "null"
            failing:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  url:
                    type: string
                  last_status:
                    type:
                      - integer
                      - "null"
                  last_error:
                    type:
                      - string
                      - "null"
                  last_attempt_at:
                    type: integer
                  delivery_status:
                    type: string
        sdk_versions:
          type: array
          items:
            type: object
            description: SDK builds that called the SDK endpoints (from X-Platform, X-Version and related headers), newest first.
      required:
        - object
        - project_id
        - checked_at
        - apps
        - webhooks
        - sdk_versions
    StoreSettings:
      type: object
      properties:
        object:
          type: string
          const: app_store_settings
        app_id:
          type: string
        type:
          type: string
        api_origin:
          type: string
          description: "This server as the outside world reaches it: the SDK's proxy URL."
        notification_url:
          type:
            - string
            - "null"
          description: App Store or Google Play notification URL for this app.
        notification_forward_url:
          type:
            - string
            - "null"
          description: Where notifications are copied during a dual run.
        last_notification_at:
          type:
            - integer
            - "null"
          format: int64
          description: Last notification processed for a known purchase. Epoch milliseconds, or null.
        last_notification_error:
          type:
            - string
            - "null"
        last_notification_received_at:
          type:
            - integer
            - "null"
          format: int64
          description: Last notification received. Epoch milliseconds, or null.
        notification_status:
          type: string
          enum:
            - ready
            - failing
            - received
            - waiting
        last_forward:
          type:
            - object
            - "null"
          properties:
            status:
              type: integer
              description: HTTP status of the forward; 0 means no answer.
            at:
              type: integer
        track_new_purchases:
          type: boolean
          description: Apply notifications about purchases this server has never seen.
        allow_unsigned_receipts:
          type: boolean
          description: Accept StoreKit 1 receipts without the in-app purchase key. Development only.
        credentials:
          type: object
          properties:
            subscription_key:
              type: object
              properties:
                configured:
                  type: boolean
                key_id:
                  type:
                    - string
                    - "null"
                issuer_id:
                  type:
                    - string
                    - "null"
            app_store_connect_api_key:
              type: object
              properties:
                configured:
                  type: boolean
                key_id:
                  type:
                    - string
                    - "null"
                issuer_id:
                  type:
                    - string
                    - "null"
                vendor_number:
                  type:
                    - string
                    - "null"
            shared_secret:
              type: object
              properties:
                configured:
                  type: boolean
            play_service_account:
              type: object
              properties:
                configured:
                  type: boolean
                client_email:
                  type:
                    - string
                    - "null"
            xcode_certificate:
              type: object
              properties:
                configured:
                  type: boolean
      required:
        - object
        - app_id
        - type
        - api_origin
        - notification_status
        - credentials
    CredentialsCheck:
      type: object
      properties:
        object:
          type: string
          const: credentials_check
        app_id:
          type: string
        store:
          type: string
        status:
          type: string
          enum:
            - valid
            - invalid
            - unreachable
        valid:
          type: boolean
        message:
          type: string
          description: What to do next, in plain words.
        checked_at:
          type: integer
          format: int64
          description: Checked at. Epoch milliseconds.
        key_id:
          type: string
        client_email:
          type:
            - string
            - "null"
      required:
        - object
        - app_id
        - store
        - status
        - valid
        - message
        - checked_at
    MassExtension:
      type: object
      properties:
        object:
          type: string
          const: subscription_mass_extension
        id:
          type: string
          description: Request id.
        app_id:
          type: string
        product_id:
          type: string
        environment:
          type: string
          enum:
            - production
            - sandbox
        extend_by_days:
          type: integer
        extend_reason_code:
          type: string
        storefront_country_codes:
          type:
            - array
            - "null"
          items:
            type: string
        complete:
          type: boolean
        completed_at:
          type:
            - integer
            - "null"
        succeeded_count:
          type:
            - integer
            - "null"
        failed_count:
          type:
            - integer
            - "null"
        requested_at:
          type: integer
      required:
        - object
        - id
        - app_id
        - product_id
        - environment
        - complete
    TestPurchase:
      type: object
      properties:
        object:
          type: string
          const: test_purchase
        scenario:
          type: string
        store_transaction_id:
          type: string
          description: The Test Store token (test_<ms>_<uuid>).
        event_types:
          type: array
          items:
            type: string
          description: Events recorded, in order.
        customer:
          $ref: "#/components/schemas/Customer"
        subscription:
          oneOf:
            - $ref: "#/components/schemas/Subscription"
            - type: "null"
        purchase:
          oneOf:
            - $ref: "#/components/schemas/Purchase"
            - type: "null"
      required:
        - object
        - scenario
        - store_transaction_id
        - event_types
        - customer
    MetricHistory:
      type: object
      properties:
        object:
          type: string
          const: metric_history
        id:
          type: string
        currency:
          type: string
          const: USD
        days:
          type: integer
        environment:
          type: string
          enum:
            - production
            - sandbox
        resolution:
          type: string
          const: day
        value:
          type: number
        previous_value:
          type:
            - number
            - "null"
        values:
          type: array
          items:
            type: object
            properties:
              date:
                type: string
                description: YYYY-MM-DD
              value:
                type: number
        last_updated_at:
          type: integer
          format: int64
          description: Computed at. Epoch milliseconds.
      required:
        - object
        - id
        - days
        - environment
        - values
    CustomerSummary:
      type: object
      properties:
        object:
          type: string
          const: customer_summary
        id:
          type: string
          description: The id you asked for.
        original_app_user_id:
          type: string
        aliases:
          type: array
          items:
            type: string
        total_revenue_in_usd:
          type: number
        sandbox_revenue_in_usd:
          type: number
        country:
          type:
            - string
            - "null"
        platform:
          type:
            - string
            - "null"
        stores:
          type: array
          items:
            type: string
        offering_override:
          type:
            - string
            - "null"
        active_entitlements:
          type: array
          items:
            type: object
        granted_entitlements:
          type: array
          items:
            type: object
        subscriptions:
          type: array
          items:
            type: object
        purchases:
          type: array
          items:
            type: object
      required:
        - object
        - id
        - original_app_user_id
    ImportResult:
      type: object
      properties:
        object:
          type: string
          const: import_result
        emit_events:
          type: boolean
        customers:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              status:
                type: string
                enum:
                  - created
                  - updated
                  - merged
              subscriptions:
                type: integer
              purchases:
                type: integer
              needs_token_refresh:
                type: integer
              notes:
                type: array
                items:
                  type: string
      required:
        - object
        - emit_events
        - customers
    ImportStatus:
      type: object
      properties:
        object:
          type: string
          const: import_status
        customers:
          type: integer
        subscriptions:
          type: integer
        needs_token_refresh:
          type: integer
          description: Google Play subscriptions still waiting for their purchase token.
        needs_token_refresh_by_app:
          type: object
          additionalProperties:
            type: integer
      required:
        - object
        - customers
        - subscriptions
        - needs_token_refresh
        - needs_token_refresh_by_app
  parameters:
    ProjectId:
      name: project_id
      in: path
      required: true
      schema:
        type: string
      description: Project id (proj...).
      example: proj18pzzkao
    AppId:
      name: app_id
      in: path
      required: true
      schema:
        type: string
      description: App id (app...).
    AppUserId:
      name: app_user_id
      in: path
      required: true
      schema:
        type: string
        maxLength: 100
      description: App user id, URL-encoded (anonymous ids look like `$RCAnonymousID:...`).
      example: user_1
    CustomerId:
      name: customer_id
      in: path
      required: true
      schema:
        type: string
      description: Any app user id of the customer.
      example: user_1
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
      description: Page size. Values outside 1-100 are clamped, not rejected.
    StartingAfter:
      name: starting_after
      in: query
      schema:
        type: string
      description: Id of the last item of the previous page. Use `next_page` instead of building it.
    Environment:
      name: environment
      in: query
      schema:
        type: string
        enum:
          - production
          - sandbox
      description: "Only this environment. Default: both."
    XPlatform:
      name: X-Platform
      in: header
      schema:
        type: string
      description: SDK platform (ios, android, macos, web ...). With a secret key it picks the project's app for that platform.
    XNonce:
      name: X-Nonce
      in: header
      schema:
        type: string
      description: Base64 nonce the SDK sends when entitlement verification is on; it is part of the signed message.
  responses:
    V2Error400:
      description: The request is invalid.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V2Error"
          example:
            object: error
            type: parameter_error
            message: "app_id: Required"
            param: app_id
            doc_url: https://revenuedot.app/docs/api/errors#parameter-error
            retryable: false
    V2Error401:
      description: No API key, or an unknown one.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V2Error"
          example:
            object: error
            type: authentication_error
            message: "Missing API key. Send Authorization: Bearer <secret key>."
            doc_url: https://revenuedot.app/docs/api/errors#authentication-error
            retryable: false
    V2Error403:
      description: The key lacks a permission, or a public key was used.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V2Error"
          example:
            object: error
            type: authorization_error
            message: API v2 requires a secret API key (sk_...). Public app keys only work with the SDK endpoints.
            doc_url: https://revenuedot.app/docs/api/errors#authorization-error
            retryable: false
    V2Error404:
      description: Not found in this project (another project's ids also answer 404).
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V2Error"
          example:
            object: error
            type: resource_missing
            message: Customer not found.
            doc_url: https://revenuedot.app/docs/api/errors#resource-missing
            retryable: false
    V2Error409:
      description: It already exists, or it conflicts with another object.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V2Error"
          example:
            object: error
            type: resource_already_exists
            message: An entitlement with lookup_key pro already exists.
            doc_url: https://revenuedot.app/docs/api/errors#resource-already-exists
            retryable: false
    V2Error422:
      description: The request is valid but cannot be done in this state or for this store.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V2Error"
          example:
            object: error
            type: unprocessable_entity_error
            message: The current offering cannot be archived. Make another offering current first.
            doc_url: https://revenuedot.app/docs/api/errors#unprocessable-entity-error
            retryable: false
    V2Error429:
      description: Too many requests. Retry later.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V2Error"
          example:
            object: error
            type: rate_limit_error
            message: This project sent too many invites today. Try again tomorrow.
            doc_url: https://revenuedot.app/docs/api/errors#rate-limit-error
            retryable: true
    V2Error503:
      description: The store could not be reached. Retry later.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V2Error"
          example:
            object: error
            type: store_error
            message: Google Play could not be reached.
            doc_url: https://revenuedot.app/docs/api/errors#store-error
            retryable: true
    V1Error400:
      description: Bad request. For receipts, a 4xx tells the SDK the purchase can never be accepted, so it finishes the transaction.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V1Error"
          example:
            code: 7103
            message: The receipt is not a valid Test Store purchase token.
    V1Error401:
      description: Unknown API key.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V1Error"
          example:
            code: 7225
            message: Invalid API Key.
    V1Error403:
      description: A public app key was used for a secret-key endpoint.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V1Error"
          example:
            code: 7225
            message: This endpoint requires a secret API key.
    V1Error404:
      description: Not found.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V1Error"
          example:
            code: 7259
            message: Subscriber not found.
    V1Error500:
      description: Server error. The SDK keeps the purchase and retries.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V1Error"
          example:
            code: 7110
            message: Internal server error.
    V1Error503:
      description: The store could not be reached. Retry later.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V1Error"
          example:
            code: 7101
            message: Google Play could not be reached.
  securitySchemes:
    publicApiKey:
      type: http
      scheme: bearer
      description: A public app key (`appl_`, `mac_`, `goog_`, `test_`, `amzn_`, `strp_`, `rcb_`, `pdl_`, `roku_`). Safe to ship in an app. The SDK sends it on every request.
    secretApiKey:
      type: http
      scheme: bearer
      description: A project secret key (`sk_...`). Server side only. Its `permissions` limit what it can do.
    dashboardSession:
      type: apiKey
      in: cookie
      name: rd_session
      description: The dashboard session cookie from `POST /auth/login`. It authorizes `/v2` for every project the user belongs to.
    googlePubSubOidc:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Google-signed OIDC token of a Pub/Sub push subscription. Checked only when the app's `pubsub_audience` credential is set.
security:
  - secretApiKey: []
externalDocs:
  description: RevenueDot documentation
  url: https://github.com/revenuedot/docs
