{
  "openapi": "3.1.0",
  "info": {
    "title": "SalesDogg API",
    "version": "0.1.0",
    "description": "SalesDogg is a subscription and in-app purchase backend. It normalizes Stripe, Braintree, App Store and\nGoogle Play into one model: **customers** own **subscriptions** and **purchases** of **products**; products\nunlock **entitlements**; **offerings** group products for display.\n\n## Authentication\nSend `Authorization: Bearer <credential>`.\n- **Secret key** (`sd_sk_...`): full access to one project. Server-side only.\n- **Public key** (`sd_pk_...`): safe in client apps; read offerings, check a user's entitlements, submit App Store / Google Play receipts, attach an anonymous user to a signed-in one, set customer attributes. It never returns attributes or purchase history.\n- **Session token** (from `salesdogg login`): a human user. Project-scoped endpoints then need an `X-Project-Id` header. Some operations need the owner or admin role in the organization; each endpoint says so.\n\n## Try it without a store account\n`POST /v1/simulator/purchases` with `{ \"app_user_id\": \"user_1\", \"store_identifier\": \"pro_monthly\" }` creates a sandbox\nsubscription with no setup. Attach its product to an entitlement, check the user's access, then drive renewals, cancellations,\nbilling issues and refunds with `POST /v1/simulator/subscriptions/{id}/advance`. Events and webhooks behave as in production.\n\n## Quickstart\n1. `POST /v1/apps` – connect a provider.\n2. `POST /v1/apps/{app_id}/import_products` (or `POST /v1/products`).\n3. `POST /v1/entitlements` with `product_ids`.\n4. `POST /v1/checkout_sessions` (web) or `POST /v1/receipts` (mobile) to record purchases.\n5. `GET /v1/customers/{app_user_id}/entitlements` to check access.\n\n## Moving from RevenueCat\n- `POST /v1/imports/revenuecat/catalog`, then `POST /v1/imports/revenuecat/customers` page by page (or `salesdogg import revenuecat`).\n- `GET /v1/subscribers/{app_user_id}` and the other `/v1/subscribers` endpoints answer in RevenueCat's REST v1 format.\n- Webhook endpoints with `format: \"revenuecat\"` receive RevenueCat's webhook body.\n- Customer attributes, aliases, `$RCAnonymousID:` ids and restore behavior (`GET/PATCH /v1/settings`) follow RevenueCat's rules.\n\n## Conventions\n- Errors: `{ \"error\": { type, code, message, param?, hint? } }`. `hint` says what to do next.\n- Creates keyed by `lookup_key`, `app_user_id` or `store_identifier` are idempotent upserts and safe to retry.\n- Lists: `limit` + `starting_after`; responses carry `has_more` and `next_cursor`.\n- Money is in minor units (cents). Timestamps are ISO 8601 UTC.\n- Rate limits: responses carry `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` (seconds). A 429 includes `Retry-After`."
  },
  "servers": [
    {
      "url": "https://api.salesdogg.com"
    }
  ],
  "components": {
    "securitySchemes": {
      "secretKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Secret API key (sd_sk_...)."
      },
      "publicKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Public API key (sd_pk_...)."
      },
      "session": {
        "type": "http",
        "scheme": "bearer",
        "description": "User session token. Requires X-Project-Id on project-scoped endpoints."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "param": {
                "type": "string"
              },
              "hint": {
                "type": "string",
                "description": "What to do next to resolve the error."
              }
            },
            "required": [
              "type",
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Organization": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "organization"
            ]
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "slug"
        ]
      },
      "Project": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "project"
            ]
          },
          "name": {
            "type": "string"
          },
          "organization_id": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          }
        },
        "required": [
          "id",
          "object",
          "name",
          "organization_id",
          "created_at"
        ]
      },
      "ApiKeyWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ApiKey"
          },
          {
            "type": "object",
            "properties": {
              "key": {
                "type": "string",
                "description": "The full key. Shown only once; store it now."
              }
            },
            "required": [
              "key"
            ]
          }
        ]
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "api_key"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "secret",
              "public"
            ]
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "description": "First characters of the key, for identification."
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 8601 timestamp (UTC)."
          }
        },
        "required": [
          "id",
          "object",
          "kind",
          "name",
          "prefix",
          "created_at",
          "last_used_at"
        ]
      },
      "App": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "app"
            ]
          },
          "provider": {
            "type": "string",
            "enum": [
              "stripe",
              "braintree",
              "app_store",
              "play_store",
              "simulator"
            ]
          },
          "name": {
            "type": "string"
          },
          "config": {
            "type": "object",
            "additionalProperties": {}
          },
          "credential_fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Names of the stored secret fields. Values are never returned."
          },
          "webhook_url": {
            "type": "string",
            "description": "Point the provider's server notifications at this URL."
          },
          "webhook_instructions": {
            "type": "string"
          },
          "setup_note": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          }
        },
        "required": [
          "id",
          "object",
          "provider",
          "name",
          "config",
          "credential_fields",
          "webhook_url",
          "webhook_instructions",
          "created_at"
        ]
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "product"
            ]
          },
          "app_id": {
            "type": "string"
          },
          "store_identifier": {
            "type": "string",
            "description": "Identifier in the store: Stripe price id, Braintree plan id, App Store product id, or Play product id (optionally \"productId:basePlanId\")."
          },
          "type": {
            "type": "string",
            "enum": [
              "subscription",
              "one_time"
            ]
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          }
        },
        "required": [
          "id",
          "object",
          "app_id",
          "store_identifier",
          "type",
          "display_name",
          "created_at"
        ]
      },
      "Entitlement": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "entitlement"
            ]
          },
          "lookup_key": {
            "type": "string",
            "description": "Stable identifier your app checks for, e.g. \"pro\"."
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "product_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          }
        },
        "required": [
          "id",
          "object",
          "lookup_key",
          "display_name",
          "product_ids",
          "created_at"
        ]
      },
      "Offering": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "offering"
            ]
          },
          "lookup_key": {
            "type": "string"
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_current": {
            "type": "boolean",
            "description": "The current offering is the default one client apps display."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {}
          },
          "packages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Package"
            }
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          }
        },
        "required": [
          "id",
          "object",
          "lookup_key",
          "display_name",
          "is_current",
          "metadata",
          "packages",
          "created_at"
        ]
      },
      "Package": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "package"
            ]
          },
          "lookup_key": {
            "type": "string"
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "products": {
            "type": "array",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/Product"
                },
                {
                  "type": "object",
                  "properties": {
                    "provider": {
                      "type": "string",
                      "enum": [
                        "stripe",
                        "braintree",
                        "app_store",
                        "play_store",
                        "simulator"
                      ]
                    }
                  },
                  "required": [
                    "provider"
                  ]
                }
              ]
            }
          }
        },
        "required": [
          "id",
          "object",
          "lookup_key",
          "display_name",
          "products"
        ]
      },
      "CustomerSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "customer"
            ]
          },
          "app_user_id": {
            "type": "string",
            "description": "The id the customer is stored under. Every id in `aliases` finds the same customer."
          },
          "original_app_user_id": {
            "type": "string",
            "description": "The first id this customer was known under (often an anonymous id)."
          },
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Other app user ids that resolve to this customer: anonymous ids merged on sign-in, ids imported from RevenueCat."
          },
          "attributes": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Customer attributes. Keys starting with \"$\" are reserved (e.g. $email, $displayName, $mediaSource)."
          },
          "created_at": {
            "type": "string",
            "description": "First seen."
          },
          "last_seen_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Last time the app (an SDK) checked in for this customer."
          },
          "last_seen_platform": {
            "type": [
              "string",
              "null"
            ],
            "description": "ios, android, macos, ... as reported by the SDK."
          },
          "last_seen_platform_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_seen_app_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_seen_sdk_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_seen_locale": {
            "type": [
              "string",
              "null"
            ],
            "description": "BCP 47, e.g. en-US."
          },
          "last_seen_country": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 3166-1 alpha-2, from the device locale."
          }
        },
        "required": [
          "id",
          "object",
          "app_user_id",
          "original_app_user_id",
          "aliases",
          "attributes",
          "created_at",
          "last_seen_at",
          "last_seen_platform",
          "last_seen_platform_version",
          "last_seen_app_version",
          "last_seen_sdk_version",
          "last_seen_locale",
          "last_seen_country"
        ]
      },
      "Customer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "customer"
            ]
          },
          "app_user_id": {
            "type": "string",
            "description": "The id the customer is stored under. Every id in `aliases` finds the same customer."
          },
          "original_app_user_id": {
            "type": "string",
            "description": "The first id this customer was known under (often an anonymous id)."
          },
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Other app user ids that resolve to this customer: anonymous ids merged on sign-in, ids imported from RevenueCat."
          },
          "attributes": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Customer attributes. Keys starting with \"$\" are reserved (e.g. $email, $displayName, $mediaSource)."
          },
          "created_at": {
            "type": "string",
            "description": "First seen."
          },
          "last_seen_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Last time the app (an SDK) checked in for this customer."
          },
          "last_seen_platform": {
            "type": [
              "string",
              "null"
            ],
            "description": "ios, android, macos, ... as reported by the SDK."
          },
          "last_seen_platform_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_seen_app_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_seen_sdk_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_seen_locale": {
            "type": [
              "string",
              "null"
            ],
            "description": "BCP 47, e.g. en-US."
          },
          "last_seen_country": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 3166-1 alpha-2, from the device locale."
          },
          "entitlements": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ActiveEntitlement"
            },
            "description": "Entitlements that are active right now."
          },
          "subscriptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Subscription"
            }
          },
          "purchases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Purchase"
            }
          }
        },
        "required": [
          "id",
          "object",
          "app_user_id",
          "original_app_user_id",
          "aliases",
          "attributes",
          "created_at",
          "last_seen_at",
          "last_seen_platform",
          "last_seen_platform_version",
          "last_seen_app_version",
          "last_seen_sdk_version",
          "last_seen_locale",
          "last_seen_country",
          "entitlements",
          "subscriptions",
          "purchases"
        ]
      },
      "ActiveEntitlement": {
        "type": "object",
        "properties": {
          "lookup_key": {
            "type": "string"
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "null means the entitlement never expires."
          },
          "will_renew": {
            "type": "boolean"
          },
          "source": {
            "type": "string",
            "enum": [
              "subscription",
              "purchase",
              "grant"
            ]
          },
          "product_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "store_product_identifier": {
            "type": [
              "string",
              "null"
            ]
          },
          "subscription_id": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "lookup_key",
          "display_name",
          "expires_at",
          "will_renew",
          "source",
          "product_id",
          "store_product_identifier",
          "subscription_id"
        ]
      },
      "Subscription": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "subscription"
            ]
          },
          "app_user_id": {
            "type": "string"
          },
          "app_id": {
            "type": "string"
          },
          "provider": {
            "type": "string",
            "enum": [
              "stripe",
              "braintree",
              "app_store",
              "play_store",
              "simulator"
            ]
          },
          "product_id": {
            "type": "string"
          },
          "store_product_identifier": {
            "type": "string"
          },
          "store_subscription_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "trialing",
              "active",
              "in_grace_period",
              "past_due",
              "paused",
              "expired"
            ],
            "description": "trialing, active and in_grace_period grant access."
          },
          "environment": {
            "type": "string",
            "enum": [
              "production",
              "sandbox"
            ]
          },
          "current_period_start": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 8601 timestamp (UTC)."
          },
          "current_period_end": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 8601 timestamp (UTC)."
          },
          "will_renew": {
            "type": "boolean"
          },
          "canceled_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 8601 timestamp (UTC)."
          },
          "refunded_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 8601 timestamp (UTC)."
          },
          "price_amount": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Price per billing period in minor units (cents)."
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "billing_period": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 8601 duration, e.g. P1M."
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          },
          "updated_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          }
        },
        "required": [
          "id",
          "object",
          "app_user_id",
          "app_id",
          "provider",
          "product_id",
          "store_product_identifier",
          "store_subscription_id",
          "status",
          "environment",
          "current_period_start",
          "current_period_end",
          "will_renew",
          "canceled_at",
          "refunded_at",
          "price_amount",
          "currency",
          "billing_period",
          "created_at",
          "updated_at"
        ]
      },
      "Purchase": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "purchase"
            ]
          },
          "app_user_id": {
            "type": "string"
          },
          "app_id": {
            "type": "string"
          },
          "provider": {
            "type": "string",
            "enum": [
              "stripe",
              "braintree",
              "app_store",
              "play_store",
              "simulator"
            ]
          },
          "product_id": {
            "type": "string"
          },
          "store_product_identifier": {
            "type": "string"
          },
          "store_transaction_id": {
            "type": "string"
          },
          "environment": {
            "type": "string",
            "enum": [
              "production",
              "sandbox"
            ]
          },
          "purchased_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          },
          "refunded_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 8601 timestamp (UTC)."
          },
          "price_amount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "object",
          "app_user_id",
          "app_id",
          "provider",
          "product_id",
          "store_product_identifier",
          "store_transaction_id",
          "environment",
          "purchased_at",
          "refunded_at",
          "price_amount",
          "currency"
        ]
      },
      "Grant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "grant"
            ]
          },
          "app_user_id": {
            "type": "string"
          },
          "entitlement": {
            "type": "string"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 8601 timestamp (UTC)."
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          }
        },
        "required": [
          "id",
          "object",
          "app_user_id",
          "entitlement",
          "expires_at",
          "created_at"
        ]
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "event"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "initial_purchase",
              "trial_started",
              "trial_converted",
              "renewal",
              "cancellation",
              "uncancellation",
              "billing_issue",
              "billing_recovered",
              "paused",
              "resumed",
              "product_change",
              "expiration",
              "refund",
              "non_renewing_purchase",
              "transfer",
              "grant_created",
              "grant_revoked",
              "test"
            ]
          },
          "app_user_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "app_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "product_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "subscription_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "purchase_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "environment": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "production",
              "sandbox",
              null
            ]
          },
          "data": {
            "type": "object",
            "additionalProperties": {}
          },
          "occurred_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          }
        },
        "required": [
          "id",
          "object",
          "type",
          "app_user_id",
          "app_id",
          "product_id",
          "subscription_id",
          "purchase_id",
          "environment",
          "data",
          "occurred_at",
          "created_at"
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "webhook_endpoint"
            ]
          },
          "url": {
            "type": "string"
          },
          "event_types": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "enum": [
                "initial_purchase",
                "trial_started",
                "trial_converted",
                "renewal",
                "cancellation",
                "uncancellation",
                "billing_issue",
                "billing_recovered",
                "paused",
                "resumed",
                "product_change",
                "expiration",
                "refund",
                "non_renewing_purchase",
                "transfer",
                "grant_created",
                "grant_revoked",
                "test"
              ]
            },
            "description": "null = all event types."
          },
          "format": {
            "type": "string",
            "enum": [
              "salesdogg",
              "revenuecat"
            ],
            "description": "`salesdogg`: the Event object. `revenuecat`: RevenueCat's webhook body ({ api_version, event }), for receivers written for RevenueCat."
          },
          "has_authorization_header": {
            "type": "boolean",
            "description": "Whether a fixed Authorization header is sent with each delivery."
          },
          "enabled": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          }
        },
        "required": [
          "id",
          "object",
          "url",
          "event_types",
          "format",
          "has_authorization_header",
          "enabled",
          "created_at"
        ]
      },
      "WebhookEndpointWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "Signing secret. Verify the `SalesDogg-Signature` header: `t=<unix>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">`."
              }
            },
            "required": [
              "secret"
            ]
          }
        ]
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "webhook_delivery"
            ]
          },
          "endpoint_id": {
            "type": "string"
          },
          "event_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "succeeded",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "last_status_code": {
            "type": [
              "integer",
              "null"
            ]
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 8601 timestamp (UTC)."
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp (UTC)."
          }
        },
        "required": [
          "id",
          "object",
          "endpoint_id",
          "event_id",
          "status",
          "attempts",
          "last_status_code",
          "last_error",
          "next_attempt_at",
          "created_at"
        ]
      },
      "MetricsPoint": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "description": "UTC day, YYYY-MM-DD. Level values (active_*, mrr) are as of the end of this day; the rest are counts during it."
          },
          "active_subscriptions": {
            "type": "integer",
            "description": "Paying subscriptions."
          },
          "active_trials": {
            "type": "integer"
          },
          "mrr": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Amount in minor units (cents) per currency code."
          },
          "new_subscriptions": {
            "type": "integer",
            "description": "Subscriptions that started paying: first purchases, converted trials and resubscriptions."
          },
          "new_trials": {
            "type": "integer"
          },
          "trial_conversions": {
            "type": "integer"
          },
          "churned_subscriptions": {
            "type": "integer",
            "description": "Paying subscriptions that expired."
          },
          "cancellations": {
            "type": "integer",
            "description": "Subscriptions set not to renew. They churn later, when the paid period ends."
          },
          "refunds": {
            "type": "integer"
          },
          "revenue": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Charges minus refunds, before store fees and taxes."
          }
        },
        "required": [
          "date",
          "active_subscriptions",
          "active_trials",
          "mrr",
          "new_subscriptions",
          "new_trials",
          "trial_conversions",
          "churned_subscriptions",
          "cancellations",
          "refunds",
          "revenue"
        ]
      },
      "Simulation": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "simulation"
            ]
          },
          "subscription": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Subscription"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ]
          },
          "purchase": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Purchase"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ]
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            },
            "description": "The lifecycle events this call produced. The same events are sent to webhook endpoints."
          }
        },
        "required": [
          "object",
          "subscription",
          "purchase",
          "events"
        ]
      },
      "ProjectSettings": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "project_settings"
            ]
          },
          "project_id": {
            "type": "string"
          },
          "restore_behavior": {
            "type": "string",
            "enum": [
              "transfer",
              "keep_with_original"
            ],
            "description": "When a purchase that belongs to one identified user is reported (restored) by another: `transfer` moves it to the new user and records a `transfer` event (default, as in RevenueCat); `keep_with_original` refuses with 409 `receipt_already_in_use`. Anonymous owners are always merged."
          }
        },
        "required": [
          "object",
          "project_id",
          "restore_behavior"
        ]
      },
      "RevenueCatSubscriber": {
        "type": "object",
        "properties": {
          "request_date": {
            "type": "string"
          },
          "request_date_ms": {
            "type": "integer"
          },
          "subscriber": {
            "type": "object",
            "properties": {
              "entitlements": {
                "type": "object",
                "additionalProperties": {},
                "description": "Every entitlement the customer has or had, keyed by lookup_key, with expires_date (null = never), product_identifier and purchase_date."
              },
              "subscriptions": {
                "type": "object",
                "additionalProperties": {},
                "description": "Keyed by store product identifier."
              },
              "non_subscriptions": {
                "type": "object",
                "additionalProperties": {}
              },
              "subscriber_attributes": {
                "type": "object",
                "additionalProperties": {
                  "type": "object",
                  "properties": {
                    "value": {
                      "type": "string"
                    },
                    "updated_at_ms": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "value",
                    "updated_at_ms"
                  ]
                }
              },
              "original_app_user_id": {
                "type": "string"
              },
              "first_seen": {
                "type": "string"
              },
              "last_seen": {
                "type": "string"
              },
              "management_url": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "entitlements",
              "subscriptions",
              "non_subscriptions",
              "subscriber_attributes",
              "original_app_user_id",
              "first_seen",
              "last_seen",
              "management_url"
            ],
            "additionalProperties": {}
          }
        },
        "required": [
          "request_date",
          "request_date_ms",
          "subscriber"
        ]
      },
      "Billing": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "billing"
            ]
          },
          "organization_id": {
            "type": "string"
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "starter",
              "pro",
              "enterprise"
            ],
            "description": "`free` is sandbox only: live payments need `starter`, `pro` or `enterprise`."
          },
          "billing_period": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "monthly",
              "annual",
              null
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Status of the SalesDogg subscription at Stripe (active, past_due, ...); null on Free."
          },
          "current_period_end": {
            "type": [
              "string",
              "null"
            ]
          },
          "live_data_since": {
            "type": [
              "string",
              "null"
            ],
            "description": "On Free: when the first production purchase or subscription was recorded (for example from Google Play). The organization should upgrade."
          },
          "upgrade_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "On Free: a link a person can open to choose a plan and pay, without signing in. Valid for 7 days; every call returns a fresh one. Show it to the user."
          },
          "plans": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "monthly_price": {
                  "type": "integer",
                  "description": "Minor units (cents), billed monthly."
                },
                "annual_price_per_month": {
                  "type": "integer",
                  "description": "Minor units (cents) per month, billed annually."
                },
                "currency": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "name",
                "monthly_price",
                "annual_price_per_month",
                "currency"
              ]
            }
          }
        },
        "required": [
          "object",
          "organization_id",
          "plan",
          "billing_period",
          "status",
          "current_period_end",
          "live_data_since",
          "upgrade_url",
          "plans"
        ]
      }
    },
    "parameters": {}
  },
  "paths": {
    "/v1/me": {
      "get": {
        "operationId": "account.whoami",
        "tags": [
          "account"
        ],
        "summary": "Identify the current credential",
        "description": "Returns the user (session) or the project (API key) the credential belongs to. Useful to verify setup.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": false,
        "x-admin-only": false,
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "credential": {
                      "type": "string",
                      "enum": [
                        "session",
                        "secret"
                      ]
                    },
                    "user": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "id",
                        "email",
                        "name"
                      ]
                    },
                    "project_id": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "credential",
                    "user",
                    "project_id"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/organizations": {
      "get": {
        "operationId": "organizations.list",
        "tags": [
          "organizations"
        ],
        "summary": "List organizations you belong to",
        "security": [
          {
            "session": []
          }
        ],
        "x-auth": [
          "session"
        ],
        "x-project-scoped": false,
        "x-admin-only": false,
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Organization"
                      }
                    }
                  },
                  "required": [
                    "object",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects": {
      "get": {
        "operationId": "projects.list",
        "tags": [
          "projects"
        ],
        "summary": "List projects you can access",
        "security": [
          {
            "session": []
          }
        ],
        "x-auth": [
          "session"
        ],
        "x-project-scoped": false,
        "x-admin-only": false,
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Project"
                      }
                    }
                  },
                  "required": [
                    "object",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "projects.create",
        "tags": [
          "projects"
        ],
        "summary": "Create a project",
        "description": "A project holds all billing data for one product. If you have no organization yet, one is created. The response includes a secret and a public API key so you can start immediately.",
        "security": [
          {
            "session": []
          }
        ],
        "x-auth": [
          "session"
        ],
        "x-project-scoped": false,
        "x-admin-only": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "organization_id": {
                    "type": "string",
                    "description": "Defaults to your only organization, or creates one named after the project."
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "project": {
                      "$ref": "#/components/schemas/Project"
                    },
                    "secret_key": {
                      "$ref": "#/components/schemas/ApiKeyWithSecret"
                    },
                    "public_key": {
                      "$ref": "#/components/schemas/ApiKeyWithSecret"
                    }
                  },
                  "required": [
                    "project",
                    "secret_key",
                    "public_key"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects/{project_id}": {
      "delete": {
        "operationId": "projects.delete",
        "tags": [
          "projects"
        ],
        "summary": "Delete a project and all of its data",
        "description": "Needs the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          }
        ],
        "x-auth": [
          "session"
        ],
        "x-project-scoped": false,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "project_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "deleted"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/api_keys": {
      "get": {
        "operationId": "api_keys.list",
        "tags": [
          "api_keys"
        ],
        "summary": "List API keys of the project",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApiKey"
                      }
                    }
                  },
                  "required": [
                    "object",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "api_keys.create",
        "tags": [
          "api_keys"
        ],
        "summary": "Create an API key",
        "description": "Secret keys (sd_sk_) have full access to the project and must stay on servers. Public keys (sd_pk_) are safe to ship in client apps: they can only read offerings, check a user's entitlements, submit App Store and Google Play receipts, attach an anonymous user to a signed-in one, and set (not read) customer attributes.\n\nSigned-in users need the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "secret",
                      "public"
                    ]
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100,
                    "default": "API key"
                  }
                },
                "required": [
                  "kind"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyWithSecret"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/api_keys/{api_key_id}": {
      "delete": {
        "operationId": "api_keys.revoke",
        "tags": [
          "api_keys"
        ],
        "summary": "Revoke an API key",
        "description": "Signed-in users need the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": true,
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "api_key_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "deleted"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps": {
      "get": {
        "operationId": "apps.list",
        "tags": [
          "apps"
        ],
        "summary": "List provider connections",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/App"
                      }
                    }
                  },
                  "required": [
                    "object",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "apps.create",
        "tags": [
          "apps"
        ],
        "summary": "Connect a payment provider",
        "description": "Creates a connection (\"app\") to one provider account or store listing. Required fields per provider:\n- stripe: credentials { secret_key, webhook_secret? }. When the API has a public HTTPS URL the webhook endpoint is registered in Stripe automatically.\n- braintree: credentials { merchant_id, public_key, private_key, environment }, config { customer_id_is_app_user_id? }.\n- app_store: config { bundle_id, app_apple_id? }, credentials { issuer_id, key_id, private_key } (optional: an In-App Purchase key, needed to look purchases up at Apple, e.g. for the RevenueCat import).\n- play_store: credentials { service_account_json }, config { package_name }.\nThe response contains `webhook_url` and `webhook_instructions` describing the one remaining manual step, if any.\nThe Free plan is sandbox only. Live credentials (a live Stripe key, Braintree `environment: production`, an App Store `app_apple_id`) on Free answer 402 `plan_required`; show the user `error.details.upgrade_url`, a link to choose a plan and pay, then retry.\n\nSigned-in users need the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "provider": {
                    "type": "string",
                    "enum": [
                      "stripe",
                      "braintree",
                      "app_store",
                      "play_store",
                      "simulator"
                    ]
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "credentials": {
                    "type": "object",
                    "additionalProperties": {},
                    "default": {},
                    "description": "Provider secrets. Stored encrypted; never returned."
                  },
                  "config": {
                    "type": "object",
                    "additionalProperties": {},
                    "default": {},
                    "description": "Non-secret provider settings."
                  }
                },
                "required": [
                  "provider"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/App"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/{app_id}": {
      "get": {
        "operationId": "apps.get",
        "tags": [
          "apps"
        ],
        "summary": "Get a provider connection",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "app_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/App"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "apps.update",
        "tags": [
          "apps"
        ],
        "summary": "Update a provider connection",
        "description": "Given credential and config fields are merged into the stored ones (e.g. set a Stripe `webhook_secret` after creating the endpoint).\n\nSigned-in users need the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": true,
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "app_id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "credentials": {
                    "type": "object",
                    "additionalProperties": {}
                  },
                  "config": {
                    "type": "object",
                    "additionalProperties": {}
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/App"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "apps.delete",
        "tags": [
          "apps"
        ],
        "summary": "Delete a provider connection and its products",
        "description": "Signed-in users need the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": true,
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "app_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "deleted"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apps/{app_id}/import_products": {
      "post": {
        "operationId": "apps.import_products",
        "tags": [
          "apps"
        ],
        "summary": "Import products from the provider",
        "description": "Fetches the provider's catalog (Stripe prices, Braintree plans) and creates any products that do not exist yet. Idempotent.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "app_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      }
                    },
                    "imported": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "object",
                    "data",
                    "imported"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/products": {
      "get": {
        "operationId": "products.list",
        "tags": [
          "products"
        ],
        "summary": "List products",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "Page size (1-100)."
            },
            "required": false,
            "description": "Page size (1-100).",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Cursor: the `next_cursor` value from the previous page."
            },
            "required": false,
            "description": "Cursor: the `next_cursor` value from the previous page.",
            "name": "starting_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "app_id",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `starting_after` to fetch the next page."
                    }
                  },
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "next_cursor"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "products.create",
        "tags": [
          "products"
        ],
        "summary": "Create a product",
        "description": "Registers a store product so it can be attached to entitlements and offerings. Idempotent on (app_id, store_identifier): creating an existing product updates and returns it.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "app_id": {
                    "type": "string"
                  },
                  "store_identifier": {
                    "type": "string",
                    "minLength": 1
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "subscription",
                      "one_time"
                    ],
                    "default": "subscription"
                  },
                  "display_name": {
                    "type": "string"
                  }
                },
                "required": [
                  "app_id",
                  "store_identifier"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/products/{product_id}": {
      "delete": {
        "operationId": "products.delete",
        "tags": [
          "products"
        ],
        "summary": "Delete a product",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "product_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "deleted"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/entitlements": {
      "get": {
        "operationId": "entitlements.list",
        "tags": [
          "entitlements"
        ],
        "summary": "List entitlements",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Entitlement"
                      }
                    }
                  },
                  "required": [
                    "object",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "entitlements.create",
        "tags": [
          "entitlements"
        ],
        "summary": "Create or update an entitlement",
        "description": "An entitlement is a level of access (e.g. \"pro\") unlocked by any of its products. Idempotent on lookup_key. When product_ids is given it replaces the attached products.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lookup_key": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "display_name": {
                    "type": "string"
                  },
                  "product_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "lookup_key"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Entitlement"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/entitlements/{entitlement}/products": {
      "post": {
        "operationId": "entitlements.attach_products",
        "tags": [
          "entitlements"
        ],
        "summary": "Attach products to an entitlement",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Entitlement id or lookup_key."
            },
            "required": true,
            "description": "Entitlement id or lookup_key.",
            "name": "entitlement",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "product_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1
                  }
                },
                "required": [
                  "product_ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Entitlement"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/entitlements/{entitlement}/products/{product_id}": {
      "delete": {
        "operationId": "entitlements.detach_product",
        "tags": [
          "entitlements"
        ],
        "summary": "Detach a product from an entitlement",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Entitlement id or lookup_key."
            },
            "required": true,
            "description": "Entitlement id or lookup_key.",
            "name": "entitlement",
            "in": "path"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "product_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Entitlement"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/entitlements/{entitlement}": {
      "delete": {
        "operationId": "entitlements.delete",
        "tags": [
          "entitlements"
        ],
        "summary": "Delete an entitlement",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Entitlement id or lookup_key."
            },
            "required": true,
            "description": "Entitlement id or lookup_key.",
            "name": "entitlement",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "deleted"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offerings": {
      "get": {
        "operationId": "offerings.list",
        "tags": [
          "offerings"
        ],
        "summary": "List offerings",
        "description": "Offerings are the sets of products you present to customers. Client apps fetch this with a public key and display the one with `is_current: true`.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          },
          {
            "publicKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret",
          "public"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Offering"
                      }
                    },
                    "current_offering_id": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "object",
                    "data",
                    "current_offering_id"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "offerings.create",
        "tags": [
          "offerings"
        ],
        "summary": "Create or update an offering",
        "description": "Idempotent on lookup_key. When `packages` is given it replaces the offering's packages. The first offering of a project becomes current automatically.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lookup_key": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100
                  },
                  "display_name": {
                    "type": "string"
                  },
                  "is_current": {
                    "type": "boolean"
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": {}
                  },
                  "packages": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "lookup_key": {
                          "type": "string",
                          "minLength": 1,
                          "description": "E.g. \"monthly\", \"annual\"."
                        },
                        "display_name": {
                          "type": "string"
                        },
                        "product_ids": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "One product per store that this package sells."
                        }
                      },
                      "required": [
                        "lookup_key",
                        "product_ids"
                      ]
                    }
                  }
                },
                "required": [
                  "lookup_key"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Offering"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/offerings/{offering}": {
      "get": {
        "operationId": "offerings.get",
        "tags": [
          "offerings"
        ],
        "summary": "Get an offering",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          },
          {
            "publicKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret",
          "public"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Offering id or lookup_key."
            },
            "required": true,
            "description": "Offering id or lookup_key.",
            "name": "offering",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Offering"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "offerings.delete",
        "tags": [
          "offerings"
        ],
        "summary": "Delete an offering",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Offering id or lookup_key."
            },
            "required": true,
            "description": "Offering id or lookup_key.",
            "name": "offering",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "deleted"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers": {
      "get": {
        "operationId": "customers.list",
        "tags": [
          "customers"
        ],
        "summary": "List and search customers",
        "description": "Newest first. Filters combine with AND. Each customer comes with attributes, aliases and last-seen device details.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "Page size (1-100)."
            },
            "required": false,
            "description": "Page size (1-100).",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Cursor: the `next_cursor` value from the previous page."
            },
            "required": false,
            "description": "Cursor: the `next_cursor` value from the previous page.",
            "name": "starting_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "An app user id or alias (exact, or the start of one), or an exact $email address (case-insensitive)."
            },
            "required": false,
            "description": "An app user id or alias (exact, or the start of one), or an exact $email address (case-insensitive).",
            "name": "search",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Only customers with this entitlement (lookup_key) active right now."
            },
            "required": false,
            "description": "Only customers with this entitlement (lookup_key) active right now.",
            "name": "entitlement",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "description": "true: customers with any active entitlement; false: customers with none."
            },
            "required": false,
            "description": "true: customers with any active entitlement; false: customers with none.",
            "name": "active",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Last seen platform, e.g. ios or android."
            },
            "required": false,
            "description": "Last seen platform, e.g. ios or android.",
            "name": "platform",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 2,
              "description": "Last seen country, ISO 3166-1 alpha-2."
            },
            "required": false,
            "description": "Last seen country, ISO 3166-1 alpha-2.",
            "name": "country",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Last seen app version."
            },
            "required": false,
            "description": "Last seen app version.",
            "name": "app_version",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "stripe",
                "braintree",
                "app_store",
                "play_store",
                "simulator"
              ],
              "description": "Customers with a subscription or purchase from this store."
            },
            "required": false,
            "description": "Customers with a subscription or purchase from this store.",
            "name": "store",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "With attribute_value: customers whose attribute equals the value (case-insensitive). Alone: customers that have the attribute."
            },
            "required": false,
            "description": "With attribute_value: customers whose attribute equals the value (case-insensitive). Alone: customers that have the attribute.",
            "name": "attribute_key",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "attribute_value",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "first_seen_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "first_seen_before",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "last_seen_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "required": false,
            "name": "last_seen_before",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CustomerSummary"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `starting_after` to fetch the next page."
                    }
                  },
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "next_cursor"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "customers.upsert",
        "tags": [
          "customers"
        ],
        "summary": "Create or update a customer",
        "description": "Idempotent on app_user_id (an alias finds its customer). Given attributes are merged into existing ones; set a key to null to remove it.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "app_user_id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                  },
                  "attributes": {
                    "type": "object",
                    "additionalProperties": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "value": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "updated_at_ms": {
                              "type": "number",
                              "description": "When the change was made on the device (ms since epoch). An older write never replaces a newer one."
                            },
                            "updated_at": {
                              "anyOf": [
                                {
                                  "type": "number"
                                },
                                {
                                  "type": "string"
                                }
                              ]
                            }
                          },
                          "required": [
                            "value"
                          ]
                        }
                      ]
                    },
                    "description": "Key -> value. A value may also be `{ value, updated_at_ms }` (RevenueCat format). null or \"\" deletes the attribute."
                  }
                },
                "required": [
                  "app_user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers/{app_user_id}/attributes": {
      "post": {
        "operationId": "customers.set_attributes",
        "tags": [
          "customers"
        ],
        "summary": "Set customer attributes",
        "description": "Same rules as RevenueCat: string values up to 500 characters; null or \"\" deletes; custom keys start with a letter, use letters, digits, \"_\" and \"-\", up to 40 characters, at most 50 per customer; keys starting with \"$\" are reserved ($email, $displayName, $phoneNumber, $mediaSource, $campaign, $adGroup, $ad, $keyword, $creative, integration ids).\nSend `{ value, updated_at_ms }` to say when the change was made; an older write never replaces a newer one.\nClient apps may call this with the public key (creating the customer if needed), but cannot read attributes back: the response then carries entitlements only. From an app, campaign attribution keys can only be set once.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          },
          {
            "publicKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret",
          "public"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Your own identifier for the user (URL-encoded). An alias works too."
            },
            "required": true,
            "description": "Your own identifier for the user (URL-encoded). An alias works too.",
            "name": "app_user_id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "attributes": {
                    "type": "object",
                    "additionalProperties": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "value": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "updated_at_ms": {
                              "type": "number",
                              "description": "When the change was made on the device (ms since epoch). An older write never replaces a newer one."
                            },
                            "updated_at": {
                              "anyOf": [
                                {
                                  "type": "number"
                                },
                                {
                                  "type": "string"
                                }
                              ]
                            }
                          },
                          "required": [
                            "value"
                          ]
                        }
                      ]
                    },
                    "description": "Key -> value. A value may also be `{ value, updated_at_ms }` (RevenueCat format). null or \"\" deletes the attribute."
                  }
                },
                "required": [
                  "attributes"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers/identify": {
      "post": {
        "operationId": "customers.identify",
        "tags": [
          "customers"
        ],
        "summary": "Attach an anonymous customer to a known user",
        "description": "Client SDKs start with an anonymous id (`$anon:<uuid>`; RevenueCat's `$RCAnonymousID:<32 hex>` is accepted too). When the person signs in, call this to move everything the anonymous customer bought, and its attributes, to `app_user_id`. The anonymous id keeps working as an alias. Safe to repeat; if the anonymous customer does not exist it only makes sure the user exists. With a public key the response contains the user's entitlements only.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          },
          {
            "publicKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret",
          "public"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "anonymous_id": {
                    "type": "string",
                    "pattern": "^(\\$anon:[0-9a-fA-F-]{36}|\\$RCAnonymousID:[0-9a-fA-F]{32})$"
                  },
                  "app_user_id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                  }
                },
                "required": [
                  "anonymous_id",
                  "app_user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers/{app_user_id}": {
      "get": {
        "operationId": "customers.get",
        "tags": [
          "customers"
        ],
        "summary": "Get a customer with entitlements, subscriptions, purchases and attributes",
        "description": "Server-side only. Client apps check access with GET /v1/customers/{app_user_id}/entitlements. An alias finds its customer.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Your own identifier for the user (URL-encoded). An alias works too."
            },
            "required": true,
            "description": "Your own identifier for the user (URL-encoded). An alias works too.",
            "name": "app_user_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "customers.delete",
        "tags": [
          "customers"
        ],
        "summary": "Delete a customer, their aliases, attributes and purchase history",
        "description": "Signed-in users need the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": true,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Your own identifier for the user (URL-encoded). An alias works too."
            },
            "required": true,
            "description": "Your own identifier for the user (URL-encoded). An alias works too.",
            "name": "app_user_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "deleted"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers/{app_user_id}/entitlements": {
      "get": {
        "operationId": "customers.entitlements",
        "tags": [
          "customers"
        ],
        "summary": "Check what a user has access to",
        "description": "The access check. Returns the entitlements that are active right now. Unknown users return an empty list rather than an error, so it is safe to call for any user. Calls from an app (public key, or any key with an X-Platform header) update the customer's last-seen details.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          },
          {
            "publicKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret",
          "public"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Your own identifier for the user (URL-encoded). An alias works too."
            },
            "required": true,
            "description": "Your own identifier for the user (URL-encoded). An alias works too.",
            "name": "app_user_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "app_user_id": {
                      "type": "string"
                    },
                    "entitlements": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ActiveEntitlement"
                      }
                    },
                    "active": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "boolean"
                      },
                      "description": "Convenience map: lookup_key -> true for every active entitlement."
                    }
                  },
                  "required": [
                    "app_user_id",
                    "entitlements",
                    "active"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers/{app_user_id}/grants": {
      "post": {
        "operationId": "customers.grant",
        "tags": [
          "customers"
        ],
        "summary": "Grant an entitlement without a purchase",
        "description": "Gives the user an entitlement directly (promotions, support, testing). Creates the customer if needed.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Your own identifier for the user (URL-encoded). An alias works too."
            },
            "required": true,
            "description": "Your own identifier for the user (URL-encoded). An alias works too.",
            "name": "app_user_id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "entitlement": {
                    "type": "string",
                    "description": "Entitlement lookup_key or id."
                  },
                  "expires_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Omit for a grant that never expires."
                  }
                },
                "required": [
                  "entitlement"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Grant"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers/{app_user_id}/grants/{entitlement}": {
      "delete": {
        "operationId": "customers.revoke_grant",
        "tags": [
          "customers"
        ],
        "summary": "Revoke granted entitlements",
        "description": "Revokes all active grants of the entitlement for this user. Access from purchases is unaffected.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Your own identifier for the user (URL-encoded). An alias works too."
            },
            "required": true,
            "description": "Your own identifier for the user (URL-encoded). An alias works too.",
            "name": "app_user_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "description": "Entitlement lookup_key or id."
            },
            "required": true,
            "description": "Entitlement lookup_key or id.",
            "name": "entitlement",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "revoked"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions": {
      "get": {
        "operationId": "subscriptions.list",
        "tags": [
          "subscriptions"
        ],
        "summary": "List subscriptions",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "Page size (1-100)."
            },
            "required": false,
            "description": "Page size (1-100).",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Cursor: the `next_cursor` value from the previous page."
            },
            "required": false,
            "description": "Cursor: the `next_cursor` value from the previous page.",
            "name": "starting_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "app_user_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "trialing",
                "active",
                "in_grace_period",
                "past_due",
                "paused",
                "expired"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "production",
                "sandbox"
              ]
            },
            "required": false,
            "name": "environment",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Subscription"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `starting_after` to fetch the next page."
                    }
                  },
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "next_cursor"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{subscription_id}": {
      "get": {
        "operationId": "subscriptions.get",
        "tags": [
          "subscriptions"
        ],
        "summary": "Get a subscription",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "subscription_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/purchases": {
      "get": {
        "operationId": "purchases.list",
        "tags": [
          "purchases"
        ],
        "summary": "List one-time purchases",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "Page size (1-100)."
            },
            "required": false,
            "description": "Page size (1-100).",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Cursor: the `next_cursor` value from the previous page."
            },
            "required": false,
            "description": "Cursor: the `next_cursor` value from the previous page.",
            "name": "starting_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "app_user_id",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Purchase"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `starting_after` to fetch the next page."
                    }
                  },
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "next_cursor"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/receipts": {
      "post": {
        "operationId": "receipts.submit",
        "tags": [
          "receipts"
        ],
        "summary": "Report a purchase made in a client app",
        "description": "Validates the purchase with the store, records it for the user and returns their updated customer record. Call it right after a purchase or restore. `token` per provider:\n- app_store: the StoreKit 2 transaction `jwsRepresentation`.\n- play_store: the purchase token (also pass `store_product_identifier` for one-time products).\n- stripe / braintree: the subscription id (links an existing subscription to the user). Secret key only: unlike a store receipt, the id does not prove who bought it.\nIf the purchase already belongs to another identified user, the project's restore behavior decides (GET/PATCH /v1/settings): `transfer` moves it to this user (a `transfer` event is recorded); `keep_with_original` refuses with 409 `receipt_already_in_use`. Purchases of anonymous users always move.\nWith a public key the response contains the user's entitlements only: `attributes`, `subscriptions` and `purchases` are empty.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          },
          {
            "publicKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret",
          "public"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "app_user_id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                  },
                  "app_id": {
                    "type": "string",
                    "description": "The app (provider connection) the purchase was made through."
                  },
                  "provider": {
                    "type": "string",
                    "enum": [
                      "stripe",
                      "braintree",
                      "app_store",
                      "play_store",
                      "simulator"
                    ],
                    "description": "Alternative to app_id when the project has exactly one app for this provider. Client SDKs send this."
                  },
                  "token": {
                    "type": "string",
                    "minLength": 1
                  },
                  "store_product_identifier": {
                    "type": "string"
                  }
                },
                "required": [
                  "app_user_id",
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/checkout_sessions": {
      "post": {
        "operationId": "checkout.create",
        "tags": [
          "checkout"
        ],
        "summary": "Create a hosted checkout page for a user",
        "description": "Returns a URL to send the user to. The resulting purchase is attributed to `app_user_id` automatically. Currently supported for Stripe products.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "app_user_id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                  },
                  "product_id": {
                    "type": "string",
                    "description": "Product to sell (a Stripe product)."
                  },
                  "success_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "cancel_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "customer_email": {
                    "type": "string",
                    "format": "email"
                  },
                  "trial_days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 730
                  }
                },
                "required": [
                  "app_user_id",
                  "product_id",
                  "success_url"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "type": "string",
                      "enum": [
                        "checkout_session"
                      ]
                    },
                    "url": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "object",
                    "url"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events": {
      "get": {
        "operationId": "events.list",
        "tags": [
          "events"
        ],
        "summary": "List lifecycle events, newest first",
        "description": "Every change to a subscription or purchase is recorded as an event, regardless of which store it came from. The same payloads are sent to webhook endpoints.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "Page size (1-100)."
            },
            "required": false,
            "description": "Page size (1-100).",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Cursor: the `next_cursor` value from the previous page."
            },
            "required": false,
            "description": "Cursor: the `next_cursor` value from the previous page.",
            "name": "starting_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "app_user_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "initial_purchase",
                "trial_started",
                "trial_converted",
                "renewal",
                "cancellation",
                "uncancellation",
                "billing_issue",
                "billing_recovered",
                "paused",
                "resumed",
                "product_change",
                "expiration",
                "refund",
                "non_renewing_purchase",
                "transfer",
                "grant_created",
                "grant_revoked",
                "test"
              ]
            },
            "required": false,
            "name": "type",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Event"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `starting_after` to fetch the next page."
                    }
                  },
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "next_cursor"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook_endpoints": {
      "get": {
        "operationId": "webhooks.list",
        "tags": [
          "webhooks"
        ],
        "summary": "List webhook endpoints",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    }
                  },
                  "required": [
                    "object",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "webhooks.create",
        "tags": [
          "webhooks"
        ],
        "summary": "Register a webhook endpoint",
        "description": "Events are POSTed as JSON and retried with exponential backoff (8 attempts over about 2 days) until your endpoint returns 2xx. The signing secret is only returned here.\nMoving from RevenueCat: set `format: \"revenuecat\"` to receive RevenueCat's webhook body, and `authorization_header` to the value your receiver already checks. Events RevenueCat has no counterpart for (billing_recovered, which RevenueCat reports as the RENEWAL that follows, and grants) are not sent to such endpoints.\n\nSigned-in users need the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "event_types": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "initial_purchase",
                        "trial_started",
                        "trial_converted",
                        "renewal",
                        "cancellation",
                        "uncancellation",
                        "billing_issue",
                        "billing_recovered",
                        "paused",
                        "resumed",
                        "product_change",
                        "expiration",
                        "refund",
                        "non_renewing_purchase",
                        "transfer",
                        "grant_created",
                        "grant_revoked",
                        "test"
                      ]
                    },
                    "minItems": 1,
                    "description": "Omit to receive all events."
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "salesdogg",
                      "revenuecat"
                    ],
                    "default": "salesdogg"
                  },
                  "authorization_header": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500,
                    "description": "Sent verbatim as the Authorization header of every delivery, as RevenueCat does."
                  }
                },
                "required": [
                  "url"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointWithSecret"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook_endpoints/{endpoint_id}": {
      "delete": {
        "operationId": "webhooks.delete",
        "tags": [
          "webhooks"
        ],
        "summary": "Delete a webhook endpoint",
        "description": "Signed-in users need the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": true,
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "endpoint_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "deleted"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook_endpoints/{endpoint_id}/test": {
      "post": {
        "operationId": "webhooks.test",
        "tags": [
          "webhooks"
        ],
        "summary": "Send a test event to all enabled endpoints",
        "description": "Emits an event of type \"test\". Check the outcome with GET /v1/webhook_deliveries.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "endpoint_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook_deliveries": {
      "get": {
        "operationId": "webhooks.deliveries",
        "tags": [
          "webhooks"
        ],
        "summary": "List webhook delivery attempts, newest first",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "description": "Page size (1-100)."
            },
            "required": false,
            "description": "Page size (1-100).",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Cursor: the `next_cursor` value from the previous page."
            },
            "required": false,
            "description": "Cursor: the `next_cursor` value from the previous page.",
            "name": "starting_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string"
            },
            "required": false,
            "name": "endpoint_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "succeeded",
                "failed"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `starting_after` to fetch the next page."
                    }
                  },
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "next_cursor"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/metrics/overview": {
      "get": {
        "operationId": "metrics.overview",
        "tags": [
          "metrics"
        ],
        "summary": "Headline numbers for the project",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "production",
                "sandbox"
              ],
              "default": "production"
            },
            "required": false,
            "name": "environment",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "environment": {
                      "type": "string",
                      "enum": [
                        "production",
                        "sandbox"
                      ]
                    },
                    "customers": {
                      "type": "integer"
                    },
                    "active_subscriptions": {
                      "type": "integer",
                      "description": "Paying subscriptions (active or in grace period)."
                    },
                    "active_trials": {
                      "type": "integer"
                    },
                    "mrr": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "currency": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "integer",
                            "description": "Minor units (cents)."
                          }
                        },
                        "required": [
                          "currency",
                          "amount"
                        ]
                      },
                      "description": "Monthly recurring revenue per currency, from paying subscriptions with a known price and billing period. Before store fees and taxes."
                    }
                  },
                  "required": [
                    "environment",
                    "customers",
                    "active_subscriptions",
                    "active_trials",
                    "mrr"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/metrics/timeseries": {
      "get": {
        "operationId": "metrics.timeseries",
        "tags": [
          "metrics"
        ],
        "summary": "Daily subscription metrics over a date range",
        "description": "One point per UTC day: active subscriptions, trials and MRR at the end of the day, plus the day's new subscriptions, trial conversions, churn, cancellations, refunds and revenue. At most 366 days per request. MRR is based on each subscription's current price.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "date",
              "description": "First day (YYYY-MM-DD, UTC). Defaults to 29 days before `to`."
            },
            "required": false,
            "description": "First day (YYYY-MM-DD, UTC). Defaults to 29 days before `to`.",
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date",
              "description": "Last day (YYYY-MM-DD, UTC). Defaults to today."
            },
            "required": false,
            "description": "Last day (YYYY-MM-DD, UTC). Defaults to today.",
            "name": "to",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "production",
                "sandbox"
              ],
              "default": "production"
            },
            "required": false,
            "name": "environment",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "environment": {
                      "type": "string",
                      "enum": [
                        "production",
                        "sandbox"
                      ]
                    },
                    "from": {
                      "type": "string"
                    },
                    "to": {
                      "type": "string"
                    },
                    "currencies": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Currencies that appear in mrr or revenue, largest MRR first."
                    },
                    "points": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MetricsPoint"
                      }
                    }
                  },
                  "required": [
                    "environment",
                    "from",
                    "to",
                    "currencies",
                    "points"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/simulator/purchases": {
      "post": {
        "operationId": "simulator.purchase",
        "tags": [
          "simulator"
        ],
        "summary": "Simulate a purchase",
        "description": "Creates a purchase without any store, so you can build and test against SalesDogg before connecting a provider. It goes through the same path as a real purchase: entitlements update, events are recorded and your webhook endpoints are called. Simulated data is always in the sandbox environment.\nName the product either way:\n- `store_identifier`: any name you like (e.g. \"pro_monthly\"). The simulator app and the product are created if they do not exist. Zero setup.\n- `product_id`: an existing product, including one imported from a real provider, to test your real catalog and entitlements without paying.\nSubscriptions start active, or trialing when `trial_days` is set. Move them on with POST /v1/simulator/subscriptions/{subscription_id}/advance.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "app_user_id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                  },
                  "store_identifier": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "product_id": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "subscription",
                      "one_time"
                    ],
                    "description": "Only with store_identifier, when the product does not exist yet. Default subscription."
                  },
                  "trial_days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 365,
                    "description": "Start a subscription with a free trial of this length."
                  },
                  "billing_period": {
                    "type": "string",
                    "pattern": "^P\\d+[DWMY]$",
                    "default": "P1M",
                    "description": "ISO 8601 duration of one billing cycle, e.g. P1M, P1Y, P1W."
                  },
                  "price_amount": {
                    "type": "integer",
                    "minimum": 0,
                    "default": 999,
                    "description": "Price per billing period in minor units (cents)."
                  },
                  "currency": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 3,
                    "default": "USD"
                  }
                },
                "required": [
                  "app_user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Simulation"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/simulator/subscriptions/{subscription_id}/advance": {
      "post": {
        "operationId": "simulator.advance",
        "tags": [
          "simulator"
        ],
        "summary": "Move a simulated subscription through its lifecycle",
        "description": "Applies one change to a subscription created by the simulator and returns the events it produced:\n- `renew`: start the next billing period (converts a trial, recovers a billing issue, resubscribes an expired subscription).\n- `cancel` / `uncancel`: turn auto-renew off or back on. Access continues until `expire`.\n- `fail_payment` / `recover`: enter and leave the billing-issue grace period.\n- `pause` / `resume`.\n- `expire`: end the subscription now; access is removed.\n- `refund`: refund the last charge and end the subscription.\nOnly simulated subscriptions can be changed this way; real ones are driven by their store.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "subscription_id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "renew",
                      "cancel",
                      "uncancel",
                      "fail_payment",
                      "recover",
                      "pause",
                      "resume",
                      "expire",
                      "refund"
                    ]
                  }
                },
                "required": [
                  "action"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Simulation"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/simulator/purchases/{purchase_id}/refund": {
      "post": {
        "operationId": "simulator.refund_purchase",
        "tags": [
          "simulator"
        ],
        "summary": "Refund a simulated one-time purchase",
        "description": "Marks the purchase refunded, which removes the access it granted.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "purchase_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Simulation"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/settings": {
      "get": {
        "operationId": "settings.get",
        "tags": [
          "settings"
        ],
        "summary": "Get the project's settings",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectSettings"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "settings.update",
        "tags": [
          "settings"
        ],
        "summary": "Change the project's settings",
        "description": "Signed-in users need the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "restore_behavior": {
                    "type": "string",
                    "enum": [
                      "transfer",
                      "keep_with_original"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectSettings"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscribers/{app_user_id}": {
      "get": {
        "operationId": "subscribers.get",
        "tags": [
          "subscribers"
        ],
        "summary": "Get (or create) a customer in RevenueCat's format",
        "description": "RevenueCat REST API v1 compatible: same path, request and response shape. Creates the customer if it does not exist, as RevenueCat does. Send X-Platform only when the request is on behalf of the app; it updates last seen. grace_period_expires_date is always null; GET /v1/customers/{app_user_id}/entitlements is the authoritative access check.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "The app user id (or any alias)."
            },
            "required": true,
            "description": "The app user id (or any alias).",
            "name": "app_user_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevenueCatSubscriber"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "subscribers.delete",
        "tags": [
          "subscribers"
        ],
        "summary": "Delete a customer (RevenueCat format)",
        "description": "RevenueCat REST API v1 compatible: same path, request and response shape. Deletes the customer, their aliases, attributes and purchase history.\n\nSigned-in users need the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": true,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "The app user id (or any alias)."
            },
            "required": true,
            "description": "The app user id (or any alias).",
            "name": "app_user_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "app_user_id": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "app_user_id"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscribers/{app_user_id}/attributes": {
      "post": {
        "operationId": "subscribers.set_attributes",
        "tags": [
          "subscribers"
        ],
        "summary": "Update customer attributes in RevenueCat's format",
        "description": "RevenueCat REST API v1 compatible: same path, request and response shape. Body: { \"attributes\": { \"<key>\": { \"value\": \"...\", \"updated_at_ms\": 1709195668093 } } }. A null or empty value deletes the attribute; an older updated_at_ms never replaces a newer value.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "The app user id (or any alias)."
            },
            "required": true,
            "description": "The app user id (or any alias).",
            "name": "app_user_id",
            "in": "path"
          }
        ],
        "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": "number"
                        }
                      },
                      "required": [
                        "value"
                      ]
                    }
                  }
                },
                "required": [
                  "attributes"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevenueCatSubscriber"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscribers/{app_user_id}/entitlements/{entitlement_identifier}/promotional": {
      "post": {
        "operationId": "subscribers.grant_promotional",
        "tags": [
          "subscribers"
        ],
        "summary": "Grant a promotional entitlement (RevenueCat format)",
        "description": "RevenueCat REST API v1 compatible: same path, request and response shape. Pass duration (daily, three_day, weekly, two_week, monthly, two_month, three_month, six_month, yearly, lifetime) or end_time_ms. start_time_ms only moves the end of a duration; the grant is active from now. Same as POST /v1/customers/{app_user_id}/grants.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "The app user id (or any alias)."
            },
            "required": true,
            "description": "The app user id (or any alias).",
            "name": "app_user_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "description": "Entitlement lookup_key."
            },
            "required": true,
            "description": "Entitlement lookup_key.",
            "name": "entitlement_identifier",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "duration": {
                    "type": "string",
                    "enum": [
                      "daily",
                      "three_day",
                      "weekly",
                      "two_week",
                      "monthly",
                      "two_month",
                      "three_month",
                      "six_month",
                      "yearly",
                      "lifetime"
                    ]
                  },
                  "start_time_ms": {
                    "type": "integer"
                  },
                  "end_time_ms": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevenueCatSubscriber"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscribers/{app_user_id}/entitlements/{entitlement_identifier}/revoke_promotionals": {
      "post": {
        "operationId": "subscribers.revoke_promotionals",
        "tags": [
          "subscribers"
        ],
        "summary": "Revoke promotional entitlements (RevenueCat format)",
        "description": "RevenueCat REST API v1 compatible: same path, request and response shape. Revokes every active grant of the entitlement. Access from purchases is unaffected.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "The app user id (or any alias)."
            },
            "required": true,
            "description": "The app user id (or any alias).",
            "name": "app_user_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "description": "Entitlement lookup_key."
            },
            "required": true,
            "description": "Entitlement lookup_key.",
            "name": "entitlement_identifier",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevenueCatSubscriber"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/imports/revenuecat/catalog": {
      "post": {
        "operationId": "imports.revenuecat_catalog",
        "tags": [
          "imports"
        ],
        "summary": "Import products, entitlements and offerings from RevenueCat",
        "description": "Step 1 of moving from RevenueCat. Connect your stores first (POST /v1/apps); RevenueCat apps are matched to them by bundle id / package name, or by store when there is one app per store.\nProducts are created under the matching app (a Stripe product becomes one product per active price), entitlements and offerings by lookup_key. Safe to run again.\n\nSigned-in users need the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "api_key": {
                    "type": "string",
                    "minLength": 1,
                    "description": "A RevenueCat REST API v2 secret key with read access (project configuration and customer information). Used for this request only; never stored."
                  },
                  "project_id": {
                    "type": "string",
                    "minLength": 1,
                    "description": "The RevenueCat project id (proj...)."
                  },
                  "app_map": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "RevenueCat app id -> SalesDogg app id, only needed when a store has several SalesDogg apps that cannot be told apart by bundle id / package name."
                  }
                },
                "required": [
                  "api_key",
                  "project_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "revenuecat_catalog_import"
                      ]
                    },
                    "apps": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "revenuecat_app_id": {
                            "type": "string"
                          },
                          "name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "type": {
                            "type": "string"
                          },
                          "app_id": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "The SalesDogg app it maps to, or null."
                          },
                          "problem": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        },
                        "required": [
                          "revenuecat_app_id",
                          "name",
                          "type",
                          "app_id",
                          "problem"
                        ]
                      }
                    },
                    "products": {
                      "type": "object",
                      "properties": {
                        "imported": {
                          "type": "integer"
                        },
                        "skipped": {
                          "type": "integer",
                          "description": "Products of apps without a SalesDogg match."
                        }
                      },
                      "required": [
                        "imported",
                        "skipped"
                      ]
                    },
                    "entitlements": {
                      "type": "object",
                      "properties": {
                        "imported": {
                          "type": "integer"
                        },
                        "product_links": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "imported",
                        "product_links"
                      ]
                    },
                    "offerings": {
                      "type": "object",
                      "properties": {
                        "imported": {
                          "type": "integer"
                        },
                        "packages": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "imported",
                        "packages"
                      ]
                    },
                    "problems": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "object",
                    "apps",
                    "products",
                    "entitlements",
                    "offerings",
                    "problems"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/imports/revenuecat/customers": {
      "post": {
        "operationId": "imports.revenuecat_customers",
        "tags": [
          "imports"
        ],
        "summary": "Import one page of customers from RevenueCat",
        "description": "Step 2, after the catalog. Call repeatedly with `starting_after: next_cursor` until `has_more` is false (`salesdogg import revenuecat` does this for you). Safe to re-run any page.\nBrings over app user ids and aliases, attributes, first/last seen and device details, and promotional entitlements (as grants).\nStore purchases are looked up at the store and recorded exactly as a store notification would be: Stripe through its API, Google Play through the Orders API, the App Store when the app has App Store Server API credentials. The rest count as `awaiting_app_sync`: the SalesDogg SDK records them on the app's next launch. Expired subscriptions are skipped unless include_expired is true.\nNothing imported sends webhooks. Point the stores' notifications at SalesDogg before importing so no change is missed in between.\n\nSigned-in users need the owner or admin role in the organization.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "api_key": {
                    "type": "string",
                    "minLength": 1,
                    "description": "A RevenueCat REST API v2 secret key with read access (project configuration and customer information). Used for this request only; never stored."
                  },
                  "project_id": {
                    "type": "string",
                    "minLength": 1,
                    "description": "The RevenueCat project id (proj...)."
                  },
                  "app_map": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "RevenueCat app id -> SalesDogg app id, only needed when a store has several SalesDogg apps that cannot be told apart by bundle id / package name."
                  },
                  "starting_after": {
                    "type": "string",
                    "description": "The next_cursor of the previous page."
                  },
                  "limit": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 50,
                    "default": 20,
                    "description": "Customers per call. Each takes about 4 RevenueCat requests plus one store lookup per purchase."
                  },
                  "include_expired": {
                    "type": "boolean",
                    "default": false,
                    "description": "Also look up subscriptions that have expired, for history."
                  }
                },
                "required": [
                  "api_key",
                  "project_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "revenuecat_customer_import"
                      ]
                    },
                    "customers": {
                      "type": "object",
                      "properties": {
                        "imported": {
                          "type": "integer"
                        },
                        "created": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "imported",
                        "created"
                      ]
                    },
                    "aliases": {
                      "type": "integer"
                    },
                    "attributes": {
                      "type": "integer"
                    },
                    "grants": {
                      "type": "integer"
                    },
                    "subscriptions": {
                      "type": "object",
                      "properties": {
                        "linked": {
                          "type": "integer"
                        },
                        "awaiting_app_sync": {
                          "type": "integer"
                        },
                        "skipped_expired": {
                          "type": "integer"
                        },
                        "failed": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "linked",
                        "awaiting_app_sync",
                        "skipped_expired",
                        "failed"
                      ]
                    },
                    "purchases": {
                      "type": "object",
                      "properties": {
                        "linked": {
                          "type": "integer"
                        },
                        "awaiting_app_sync": {
                          "type": "integer"
                        },
                        "skipped_refunded": {
                          "type": "integer"
                        },
                        "failed": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "linked",
                        "awaiting_app_sync",
                        "skipped_refunded",
                        "failed"
                      ]
                    },
                    "problems": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "revenuecat_customer_id": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "revenuecat_customer_id",
                          "message"
                        ]
                      },
                      "description": "Up to 100 things that were skipped, and why."
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "object",
                    "customers",
                    "aliases",
                    "attributes",
                    "grants",
                    "subscriptions",
                    "purchases",
                    "problems",
                    "has_more",
                    "next_cursor"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing": {
      "get": {
        "operationId": "billing.get",
        "tags": [
          "billing"
        ],
        "summary": "Get the organization's SalesDogg plan, with an upgrade link on Free",
        "description": "Free is sandbox only. On Free, `upgrade_url` is a link a person can open to choose a plan and pay without signing in; show it to the user when they want to go live. Connecting live credentials on Free answers 402 `plan_required` with the same link in `error.details.upgrade_url`.",
        "security": [
          {
            "session": []
          },
          {
            "secretKey": []
          }
        ],
        "x-auth": [
          "session",
          "secret"
        ],
        "x-project-scoped": true,
        "x-admin-only": false,
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; see the Retry-After header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {}
}
