{"openapi": "3.0.3", "info": {"title": "Qonversion Public Analytics API", "description": "Public API for accessing Qonversion analytics chart data and customer information.\n\n## Quick Start\n\n1. Find your **Project API Key** in the Qonversion dashboard.\n2. Use it in the URL path of any analytics endpoint, for example:\n   `https://api.qonversion.io/v1/analytics/{YOUR_PROJECT_API_KEY}/chart/proceeds`.\n3. No additional authentication headers are required.\n\n## Where to find your API Key\n\n1. Sign in to the Qonversion dashboard at https://dash.qonversion.io/.\n2. Open your project, then go to **Project Settings** in the left sidebar.\n3. In the **Project Keys** section, copy the value labelled **API Key**.\n   The **Project Key** field above it is the SDK key and will not authorize this API.\n\nThe API Key is a 32-character alphanumeric string (for example, `JFPATc4VaaWYsfurml3qZ4zsmNw0VfWH`). It contains no fixed prefix.\n\n## Authentication\n\nAuthentication is done via the **Project API Key** in the URL path. This key both identifies your project and authorizes access to its analytics data. No `Authorization` header or query token is needed.\n\n**Sandbox mode is not supported by this API.** Project API Keys prefixed with `test_` are accepted, but the prefix is stripped and the request is processed as production. Use your production key.\n\n**Security:** Treat your Project API Key as a secret. Do not embed it in client-side or mobile-app code, and do not commit it to public repositories.\n\n## Rate Limiting\n\n- **Limit:** 30 requests per minute per project (`Analytics` rate limit).\n- **Response:** HTTP 429 when the limit is exceeded. The 429 response carries `Retry-After: 60` so clients can honour the standard back-off header.\n- **Cache fallback:** If the gateway has a recent successful response cached for the same path + query, it is returned instead of 429 with header `X-Cache: HIT`. Caches expire after 5 minutes.\n- **Recommendation:** Honour `Retry-After` and implement exponential backoff in your client.\n\n## Available Charts\n\nFour chart endpoints are exposed publicly:\n\n- **`proceeds`** - revenue and proceeds metrics. Supports all filters and segmentation.\n- **`users-overview`** - user acquisition and retention. Supports user filters and segmentation. Does NOT support product filters.\n- **`paid-subscriptions-movement`** - subscription transitions (new, renewed, churned). Supports common + product filters only. Does NOT support segmentation, `filter[user_id]`, or `filter[device_id]`.\n- **`active-subscriptions`** - currently active subscribers. Supports product filters and segmentation. Does NOT support `filter[user_id]` or `filter[device_id]`.\n\n## Client Generation\n\nThis OpenAPI specification is designed for generating type-safe clients. Tools you can use:\n- [openapi-generator](https://openapi-generator.tech/)\n- [swagger-codegen](https://swagger.io/tools/swagger-codegen/)\n\n## Example Request\n\n```bash\ncurl \"https://api.qonversion.io/v1/analytics/JFPATc4VaaWYsfurml3qZ4zsmNw0VfWH/chart/proceeds?from=1699000000&to=1701000000&unit=day&environment=1\"\n```\n\nReplace the path segment with your own 32-character API Key.\n\n## Response Format\n\n**Success (200):**\n```json\n{\n  \"data\": {\n    \"code\": \"proceeds\",\n    \"from\": 1699000000,\n    \"to\": 1701000000,\n    \"unit\": \"day\",\n    \"environment\": 1,\n    \"currency\": \"USD\",\n    \"series\": []\n  }\n}\n```\n\n**Error (4xx/5xx):**\n```json\n{\n  \"error\": {\n    \"code\": \"validation_error\",\n    \"message\": \"Unprocessable request params\",\n    \"type\": \"request\"\n  },\n  \"meta\": {\n    \"reference\": \"https://documentation.qonversion.io/reference/handling-api-errors\"\n  }\n}\n```\n", "version": "1.0.0", "contact": {"name": "Qonversion API Support", "url": "https://documentation.qonversion.io", "email": "support@qonversion.io"}}, "servers": [{"url": "https://api.qonversion.io/v1/analytics", "description": "Production server"}], "tags": [{"name": "Analytics Charts", "description": "Dedicated endpoints for each analytics chart type"}, {"name": "Customers", "description": "Endpoints for retrieving customer data and aggregated customer metrics"}], "paths": {"/{projectApiKey}/chart/proceeds": {"get": {"summary": "Get proceeds chart data", "description": "Retrieve proceeds analytics. All filters and segmentation supported.\n", "operationId": "getProceedsChart", "tags": ["Analytics Charts"], "parameters": [{"$ref": "#/components/parameters/ProjectApiKey"}, {"$ref": "#/components/parameters/FromTimestamp"}, {"$ref": "#/components/parameters/ToTimestamp"}, {"$ref": "#/components/parameters/TimeUnit"}, {"$ref": "#/components/parameters/Environment"}, {"$ref": "#/components/parameters/MaxSeries"}, {"$ref": "#/components/parameters/Segmentation"}, {"$ref": "#/components/parameters/FilterTargetPlatform"}, {"$ref": "#/components/parameters/FilterCountry"}, {"$ref": "#/components/parameters/FilterLocale"}, {"$ref": "#/components/parameters/FilterModel"}, {"$ref": "#/components/parameters/FilterOsVersion"}, {"$ref": "#/components/parameters/FilterSdkVersion"}, {"$ref": "#/components/parameters/FilterQUserId"}, {"$ref": "#/components/parameters/FilterProductId"}, {"$ref": "#/components/parameters/FilterPurchaseCurrency"}, {"$ref": "#/components/parameters/FilterAppVersion"}, {"$ref": "#/components/parameters/FilterUserId"}, {"$ref": "#/components/parameters/FilterDeviceId"}, {"$ref": "#/components/parameters/FilterMediaSourceName"}, {"$ref": "#/components/parameters/FilterCampaignName"}, {"$ref": "#/components/parameters/FilterAdSetName"}, {"$ref": "#/components/parameters/FilterAdName"}, {"$ref": "#/components/parameters/FilterEventId"}, {"$ref": "#/components/parameters/FilterExperimentUid"}, {"$ref": "#/components/parameters/FilterExperimentGroupUid"}], "responses": {"200": {"description": "Successful response with chart data", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChartResponse"}}}}, "400": {"description": "Bad request - invalid parameters", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "401": {"description": "Unauthorized - invalid or missing Project API Key", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "429": {"description": "Too many requests - rate limit exceeded", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "500": {"description": "Internal server error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}}}}, "/{projectApiKey}/chart/users-overview": {"get": {"summary": "Get users overview chart data", "description": "Retrieve users overview analytics. User filters and segmentation, no product filters.\n", "operationId": "getUsersOverviewChart", "tags": ["Analytics Charts"], "parameters": [{"$ref": "#/components/parameters/ProjectApiKey"}, {"$ref": "#/components/parameters/FromTimestamp"}, {"$ref": "#/components/parameters/ToTimestamp"}, {"$ref": "#/components/parameters/TimeUnit"}, {"$ref": "#/components/parameters/Environment"}, {"$ref": "#/components/parameters/MaxSeries"}, {"$ref": "#/components/parameters/Segmentation"}, {"$ref": "#/components/parameters/FilterTargetPlatform"}, {"$ref": "#/components/parameters/FilterCountry"}, {"$ref": "#/components/parameters/FilterLocale"}, {"$ref": "#/components/parameters/FilterModel"}, {"$ref": "#/components/parameters/FilterOsVersion"}, {"$ref": "#/components/parameters/FilterSdkVersion"}, {"$ref": "#/components/parameters/FilterQUserId"}, {"$ref": "#/components/parameters/FilterUserId"}, {"$ref": "#/components/parameters/FilterDeviceId"}, {"$ref": "#/components/parameters/FilterMediaSourceName"}, {"$ref": "#/components/parameters/FilterCampaignName"}, {"$ref": "#/components/parameters/FilterAdSetName"}, {"$ref": "#/components/parameters/FilterAdName"}], "responses": {"200": {"description": "Successful response with chart data", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChartResponse"}}}}, "400": {"description": "Bad request - invalid parameters", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "401": {"description": "Unauthorized - invalid or missing Project API Key", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "429": {"description": "Too many requests - rate limit exceeded", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "500": {"description": "Internal server error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}}}}, "/{projectApiKey}/chart/paid-subscriptions-movement": {"get": {"summary": "Get paid subscriptions movement chart data", "description": "Retrieve paid subscriptions movement analytics. Common + product filters only. Segmentation, filter[user_id], and filter[device_id] are NOT applied.\n", "operationId": "getPaidSubscriptionsMovementChart", "tags": ["Analytics Charts"], "parameters": [{"$ref": "#/components/parameters/ProjectApiKey"}, {"$ref": "#/components/parameters/FromTimestamp"}, {"$ref": "#/components/parameters/ToTimestamp"}, {"$ref": "#/components/parameters/TimeUnit"}, {"$ref": "#/components/parameters/Environment"}, {"$ref": "#/components/parameters/MaxSeries"}, {"$ref": "#/components/parameters/FilterTargetPlatform"}, {"$ref": "#/components/parameters/FilterCountry"}, {"$ref": "#/components/parameters/FilterLocale"}, {"$ref": "#/components/parameters/FilterModel"}, {"$ref": "#/components/parameters/FilterOsVersion"}, {"$ref": "#/components/parameters/FilterSdkVersion"}, {"$ref": "#/components/parameters/FilterQUserId"}, {"$ref": "#/components/parameters/FilterProductId"}, {"$ref": "#/components/parameters/FilterPurchaseCurrency"}, {"$ref": "#/components/parameters/FilterAppVersion"}, {"$ref": "#/components/parameters/FilterMediaSourceName"}, {"$ref": "#/components/parameters/FilterCampaignName"}, {"$ref": "#/components/parameters/FilterAdSetName"}, {"$ref": "#/components/parameters/FilterAdName"}], "responses": {"200": {"description": "Successful response with chart data", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChartResponse"}}}}, "400": {"description": "Bad request - invalid parameters", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "401": {"description": "Unauthorized - invalid or missing Project API Key", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "429": {"description": "Too many requests - rate limit exceeded", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "500": {"description": "Internal server error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}}}}, "/{projectApiKey}/chart/active-subscriptions": {"get": {"summary": "Get active subscriptions chart data", "description": "Retrieve active subscriptions analytics. Product filters and segmentation. filter[user_id] and filter[device_id] are NOT applied.\n", "operationId": "getActiveSubscriptionsChart", "tags": ["Analytics Charts"], "parameters": [{"$ref": "#/components/parameters/ProjectApiKey"}, {"$ref": "#/components/parameters/FromTimestamp"}, {"$ref": "#/components/parameters/ToTimestamp"}, {"$ref": "#/components/parameters/TimeUnit"}, {"$ref": "#/components/parameters/Environment"}, {"$ref": "#/components/parameters/MaxSeries"}, {"$ref": "#/components/parameters/Segmentation"}, {"$ref": "#/components/parameters/FilterTargetPlatform"}, {"$ref": "#/components/parameters/FilterCountry"}, {"$ref": "#/components/parameters/FilterLocale"}, {"$ref": "#/components/parameters/FilterModel"}, {"$ref": "#/components/parameters/FilterOsVersion"}, {"$ref": "#/components/parameters/FilterSdkVersion"}, {"$ref": "#/components/parameters/FilterQUserId"}, {"$ref": "#/components/parameters/FilterProductId"}, {"$ref": "#/components/parameters/FilterPurchaseCurrency"}, {"$ref": "#/components/parameters/FilterAppVersion"}, {"$ref": "#/components/parameters/FilterMediaSourceName"}, {"$ref": "#/components/parameters/FilterCampaignName"}, {"$ref": "#/components/parameters/FilterAdSetName"}, {"$ref": "#/components/parameters/FilterAdName"}], "responses": {"200": {"description": "Successful response with chart data", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChartResponse"}}}}, "400": {"description": "Bad request - invalid parameters", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "401": {"description": "Unauthorized - invalid or missing Project API Key", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "429": {"description": "Too many requests - rate limit exceeded", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "500": {"description": "Internal server error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}}}}, "/{projectApiKey}/customers": {"get": {"summary": "Get customers list", "description": "Retrieve a paginated list of customers with optional filters and search.\n\nExample:\n```bash\ncurl \"https://api.qonversion.io/v1/analytics/JFPATc4VaaWYsfurml3qZ4zsmNw0VfWH/customers?page=1&limit=20&sort_by=since&sort_order=desc&environment=1\"\n```\n\n**Pagination:** Pages are 1-based at the public boundary. `page=1` returns the first page. Compute `total_pages` from the response as `ceil(total_count / limit)`; the response itself does not contain a `total_pages` field.\n", "operationId": "getCustomersList", "tags": ["Customers"], "parameters": [{"$ref": "#/components/parameters/ProjectApiKey"}, {"name": "page", "in": "query", "required": false, "description": "Page number (1-based). Page 1 is the first page.", "schema": {"type": "integer", "minimum": 1, "default": 1}}, {"name": "limit", "in": "query", "required": false, "description": "Items per page. Capped at 100.", "schema": {"type": "integer", "minimum": 1, "maximum": 100, "default": 20}}, {"name": "sort_by", "in": "query", "required": false, "description": "Field to sort by. Allowed values are tied to the underlying customer index.", "schema": {"type": "string", "enum": ["since", "status", "last_payment_at", "purchase_currency", "payments_count", "net_payments_usd", "purchase_product_id"], "default": "since"}}, {"name": "sort_order", "in": "query", "required": false, "description": "Sort direction.", "schema": {"type": "string", "enum": ["asc", "desc"], "default": "desc"}}, {"name": "environment", "in": "query", "required": false, "description": "Environment type (0=Sandbox, 1=Production). Sandbox keys are not supported by this API; production keys returning environment=0 will yield no data.", "schema": {"type": "integer", "enum": [0, 1], "default": 1}}, {"name": "search", "in": "query", "required": false, "description": "Universal search across email, transaction id, firebase id, custom user id, device id, and Qonversion user id. Forwarded to the backend as `search_all`.", "schema": {"type": "string", "maxLength": 255}}, {"name": "search_key", "in": "query", "required": false, "description": "Targeted search field. Use together with `search_value`. Forwarded as `advanced_key`.", "schema": {"type": "string", "enum": ["email", "firebaseID", "transactionID", "customerUserID", "deviceID"]}}, {"name": "search_value", "in": "query", "required": false, "description": "Value to match for the targeted `search_key`. Forwarded as `advanced_value`.", "schema": {"type": "string"}}, {"$ref": "#/components/parameters/FilterSinceRange"}, {"$ref": "#/components/parameters/FilterStatus"}, {"$ref": "#/components/parameters/FilterTargetPlatform"}, {"$ref": "#/components/parameters/FilterCountry"}, {"$ref": "#/components/parameters/FilterProductId"}, {"$ref": "#/components/parameters/FilterPurchaseCurrency"}, {"$ref": "#/components/parameters/FilterLocale"}, {"$ref": "#/components/parameters/FilterModel"}, {"$ref": "#/components/parameters/FilterOsVersion"}, {"$ref": "#/components/parameters/FilterAppVersion"}, {"$ref": "#/components/parameters/FilterSdkVersion"}, {"$ref": "#/components/parameters/FilterMediaSourceName"}, {"$ref": "#/components/parameters/FilterCampaignName"}, {"$ref": "#/components/parameters/FilterAdSetName"}, {"$ref": "#/components/parameters/FilterAdName"}, {"$ref": "#/components/parameters/FilterExperimentUid"}, {"$ref": "#/components/parameters/FilterExperimentGroupUid"}], "responses": {"200": {"description": "Successful response with paginated customer list", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomersListResponse"}}}}, "400": {"description": "Bad request - invalid parameters", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "401": {"description": "Unauthorized - invalid or missing Project API Key", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "429": {"description": "Too many requests - rate limit exceeded", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "500": {"description": "Internal server error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}}}}, "/{projectApiKey}/customers-metrics": {"get": {"summary": "Get customer metrics (Analytics)", "description": "Retrieve aggregated customer metrics for a project. Accepts the same filters and `search` as the customers list (no `page`, `limit`, or sort).\n\nReturns counts (active subscribers, churned subscribers, active trials, billing-retry users), percentages (cancelled-trial rate, churned-subscriber rate), monetary aggregates (total sales, average price), and average payment count.\n", "operationId": "getCustomerMetrics", "tags": ["Customers"], "parameters": [{"$ref": "#/components/parameters/ProjectApiKey"}, {"name": "environment", "in": "query", "required": false, "description": "Environment type (0=Sandbox, 1=Production).", "schema": {"type": "integer", "enum": [0, 1], "default": 1}}, {"name": "search", "in": "query", "required": false, "description": "Universal search (same semantics as `/customers`).", "schema": {"type": "string", "maxLength": 255}}, {"$ref": "#/components/parameters/FilterSinceRange"}, {"$ref": "#/components/parameters/FilterStatus"}, {"$ref": "#/components/parameters/FilterTargetPlatform"}, {"$ref": "#/components/parameters/FilterCountry"}, {"$ref": "#/components/parameters/FilterProductId"}, {"$ref": "#/components/parameters/FilterPurchaseCurrency"}, {"$ref": "#/components/parameters/FilterLocale"}, {"$ref": "#/components/parameters/FilterModel"}, {"$ref": "#/components/parameters/FilterOsVersion"}, {"$ref": "#/components/parameters/FilterAppVersion"}, {"$ref": "#/components/parameters/FilterSdkVersion"}, {"$ref": "#/components/parameters/FilterMediaSourceName"}, {"$ref": "#/components/parameters/FilterCampaignName"}, {"$ref": "#/components/parameters/FilterAdSetName"}, {"$ref": "#/components/parameters/FilterAdName"}, {"$ref": "#/components/parameters/FilterExperimentUid"}, {"$ref": "#/components/parameters/FilterExperimentGroupUid"}], "responses": {"200": {"description": "Successful response with aggregated customer metrics", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomerMetricsResponse"}}}}, "400": {"description": "Bad request - invalid parameters", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "401": {"description": "Unauthorized - invalid or missing Project API Key", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "429": {"description": "Too many requests - rate limit exceeded", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}, "500": {"description": "Internal server error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}}}}}}}}, "components": {"parameters": {"ProjectApiKey": {"name": "projectApiKey", "in": "path", "required": true, "description": "Project API Key for authentication and project identification. A 32-character alphanumeric string copied from Project Settings -> Project Keys -> API Key in https://dash.qonversion.io/. The key has no fixed prefix.", "schema": {"type": "string", "minLength": 1, "example": "JFPATc4VaaWYsfurml3qZ4zsmNw0VfWH"}}, "FromTimestamp": {"name": "from", "in": "query", "required": false, "description": "Start of time range (Unix timestamp, seconds).", "schema": {"type": "integer", "format": "int64"}}, "ToTimestamp": {"name": "to", "in": "query", "required": false, "description": "End of time range (Unix timestamp, seconds). Must be greater than `from`.", "schema": {"type": "integer", "format": "int64"}}, "TimeUnit": {"name": "unit", "in": "query", "required": false, "description": "Time grouping unit.", "schema": {"type": "string", "enum": ["hour", "day", "week", "month"], "default": "day"}}, "Environment": {"name": "environment", "in": "query", "required": false, "description": "Environment type (0=Sandbox, 1=Production). Sandbox keys are not supported by this API; production keys requesting environment=0 will return no data.", "schema": {"type": "integer", "enum": [0, 1], "default": 1}}, "MaxSeries": {"name": "max_series", "in": "query", "required": false, "description": "Maximum number of series to return.", "schema": {"type": "integer", "minimum": 0, "maximum": 500, "default": 50}}, "Segmentation": {"name": "segmentation", "in": "query", "required": false, "description": "Group by dimension. Not all charts support every segmentation; passing an unsupported value returns 400 with an explanatory message.", "schema": {"type": "string"}}, "FilterTargetPlatform": {"name": "filter[target_platform][]", "in": "query", "required": false, "description": "Filter by platforms.", "schema": {"type": "array", "items": {"type": "string", "enum": ["iOS", "Android", "Stripe"]}}, "style": "form", "explode": true}, "FilterCountry": {"name": "filter[country][]", "in": "query", "required": false, "description": "Filter by countries (ISO 3166-1 alpha-2).", "schema": {"type": "array", "items": {"type": "string", "pattern": "^[A-Z]{2}$"}}, "style": "form", "explode": true}, "FilterLocale": {"name": "filter[locale][]", "in": "query", "required": false, "description": "Filter by locales.", "schema": {"type": "array", "items": {"type": "string"}}, "style": "form", "explode": true}, "FilterModel": {"name": "filter[model][]", "in": "query", "required": false, "description": "Filter by device models.", "schema": {"type": "array", "items": {"type": "string"}}, "style": "form", "explode": true}, "FilterOsVersion": {"name": "filter[os_version][]", "in": "query", "required": false, "description": "Filter by OS versions.", "schema": {"type": "array", "items": {"type": "string"}}, "style": "form", "explode": true}, "FilterSdkVersion": {"name": "filter[sdk_version][]", "in": "query", "required": false, "description": "Filter by SDK versions.", "schema": {"type": "array", "items": {"type": "string"}}, "style": "form", "explode": true}, "FilterQUserId": {"name": "filter[q_user_id]", "in": "query", "required": false, "description": "Filter by Qonversion user ID.", "schema": {"type": "string"}}, "FilterProductId": {"name": "filter[product_id][]", "in": "query", "required": false, "description": "Filter by product IDs.", "schema": {"type": "array", "items": {"type": "string"}}, "style": "form", "explode": true}, "FilterPurchaseCurrency": {"name": "filter[purchase_currency][]", "in": "query", "required": false, "description": "Filter by purchase currencies (ISO 4217).", "schema": {"type": "array", "items": {"type": "string", "pattern": "^[A-Z]{3}$"}}, "style": "form", "explode": true}, "FilterAppVersion": {"name": "filter[app_version][]", "in": "query", "required": false, "description": "Filter by app versions.", "schema": {"type": "array", "items": {"type": "string"}}, "style": "form", "explode": true}, "FilterUserId": {"name": "filter[user_id]", "in": "query", "required": false, "description": "Filter by custom user ID.", "schema": {"type": "string"}}, "FilterDeviceId": {"name": "filter[device_id]", "in": "query", "required": false, "description": "Filter by device ID.", "schema": {"type": "string"}}, "FilterMediaSourceName": {"name": "filter[media_source_name][]", "in": "query", "required": false, "description": "Filter by media source names.", "schema": {"type": "array", "items": {"type": "string"}}, "style": "form", "explode": true}, "FilterCampaignName": {"name": "filter[campaign_name][]", "in": "query", "required": false, "description": "Filter by campaign names.", "schema": {"type": "array", "items": {"type": "string"}}, "style": "form", "explode": true}, "FilterAdSetName": {"name": "filter[ad_set_name][]", "in": "query", "required": false, "description": "Filter by ad set names.", "schema": {"type": "array", "items": {"type": "string"}}, "style": "form", "explode": true}, "FilterAdName": {"name": "filter[ad_name][]", "in": "query", "required": false, "description": "Filter by ad names.", "schema": {"type": "array", "items": {"type": "string"}}, "style": "form", "explode": true}, "FilterEventId": {"name": "filter[event_id][]", "in": "query", "required": false, "description": "Filter by event IDs.", "schema": {"type": "array", "items": {"type": "integer"}}, "style": "form", "explode": true}, "FilterExperimentUid": {"name": "filter[experiment_uid][]", "in": "query", "required": false, "description": "Filter by experiment UIDs.", "schema": {"type": "array", "items": {"type": "string"}}, "style": "form", "explode": true}, "FilterExperimentGroupUid": {"name": "filter[experiment_group_uid][]", "in": "query", "required": false, "description": "Filter by experiment group UIDs.", "schema": {"type": "array", "items": {"type": "string"}}, "style": "form", "explode": true}, "FilterStatus": {"name": "filter[status][]", "in": "query", "required": false, "description": "Filter by customer status. Numeric codes:\n- 1 = Trial / Intro\n- 2 = Active\n- 3 = Canceled\n- 4 = Billing Retry\n", "schema": {"type": "array", "items": {"type": "string", "enum": ["1", "2", "3", "4"]}}, "style": "form", "explode": true}, "FilterSinceRange": {"name": "filter[since][]", "in": "query", "required": false, "description": "Date-range filter on customer first-seen time. Pass exactly two values: `filter[since][]=<from_unix_ts>&filter[since][]=<to_unix_ts>`. If omitted, both bounds default to the current time and the result will be empty.", "schema": {"type": "array", "items": {"type": "integer", "format": "int64"}, "minItems": 2, "maxItems": 2}, "style": "form", "explode": true}}, "schemas": {"ChartResponse": {"type": "object", "required": ["data"], "properties": {"data": {"$ref": "#/components/schemas/ChartData"}}}, "ChartData": {"type": "object", "required": ["code", "from", "to", "unit", "environment", "seriesRelation", "maxSeries", "totalType", "measure", "currency", "series"], "properties": {"code": {"type": "string", "description": "Chart type identifier (echoes request).", "example": "proceeds"}, "from": {"type": "integer", "format": "int64", "description": "Start of time range (Unix timestamp).", "example": 1699000000}, "to": {"type": "integer", "format": "int64", "description": "End of time range (Unix timestamp).", "example": 1701000000}, "unit": {"type": "string", "enum": ["hour", "day", "week", "month"], "description": "Time grouping unit echoed from the request.", "example": "day"}, "environment": {"type": "integer", "enum": [0, 1], "description": "Environment (0=Sandbox, 1=Production).", "example": 1}, "seriesRelation": {"type": "string", "enum": ["partsOfWhole", "independent"], "description": "Relationship between series (`partsOfWhole` = sum to total, `independent` = separate).", "example": "partsOfWhole"}, "maxSeries": {"type": "integer", "description": "Maximum series count.", "example": 50}, "totalType": {"type": "string", "enum": ["sum", "wavg"], "description": "Total calculation type (sum or weighted average).", "example": "sum"}, "measure": {"type": "string", "enum": ["usd", "count", "percent"], "description": "Measurement unit.", "example": "usd"}, "currency": {"type": "string", "description": "Currency code for the chart (typically `USD` for monetary measures, empty string otherwise).", "example": "USD"}, "horizontalLabelType": {"type": "string", "nullable": true, "description": "Optional label format hint for the horizontal axis. Present only when set by the chart executor.", "example": "default"}, "segmentation": {"type": "string", "nullable": true, "description": "Segmentation dimension used. Omitted when no segmentation was applied.", "example": "country"}, "summarySeries": {"$ref": "#/components/schemas/Series", "nullable": true, "description": "Summary series. Present only when the chart is segmented and a roll-up exists."}, "series": {"type": "array", "description": "Data series array.", "items": {"$ref": "#/components/schemas/Series"}}}}, "Series": {"type": "object", "required": ["label", "data"], "properties": {"label": {"type": "string", "description": "Series name.", "example": "After refunds"}, "total": {"type": "number", "format": "double", "nullable": true, "description": "Total value for the period (rounded to 2 decimals).", "example": 380276.24}, "totalPrev": {"type": "number", "format": "double", "nullable": true, "description": "Total value for the previous period (rounded to 2 decimals).", "example": 326882.25}, "totalWeight": {"type": "number", "format": "double", "nullable": true, "description": "Weight for weighted-average calculations."}, "totalPrevWeight": {"type": "number", "format": "double", "nullable": true, "description": "Previous-period weight for weighted-average calculations."}, "totalIgnoreInFinal": {"type": "boolean", "nullable": true, "description": "Whether to ignore this series in final calculations. Only present when set."}, "data": {"type": "array", "description": "Array of data points.", "items": {"$ref": "#/components/schemas/DataPoint"}}}}, "DataPoint": {"type": "object", "required": ["start_time", "value"], "properties": {"start_time": {"type": "integer", "format": "int64", "description": "Period start time (Unix timestamp).", "example": 1699000000}, "value": {"type": "number", "format": "double", "description": "Metric value (rounded to 2 decimals).", "example": 6163.49}}}, "ErrorResponse": {"type": "object", "required": ["error"], "properties": {"error": {"type": "object", "required": ["code", "message", "type"], "properties": {"code": {"type": "string", "description": "Machine-readable error code.", "example": "validation_error"}, "message": {"type": "string", "description": "Human-readable error message.", "example": "Unprocessable request params"}, "type": {"type": "string", "description": "High-level error class.", "example": "request"}}}, "meta": {"type": "object", "nullable": true, "description": "Optional metadata. May contain a `reference` URL pointing to documentation for the error code.", "additionalProperties": true, "example": {"reference": "https://documentation.qonversion.io/reference/handling-api-errors"}}}}, "Customer": {"type": "object", "description": "Customer entry as returned by `/customers`. Field names are kept stable for backward compatibility with the dashboard.", "properties": {"client_id": {"type": "integer", "format": "int64", "description": "Internal numeric customer ID.", "example": 12345}, "client_uid": {"type": "string", "nullable": true, "description": "Qonversion user ID (the SDK-issued user UID). May be empty when the underlying ClickHouse aggregate (`any(uid)`) returns no value for the customer.", "example": "QON_abc123def456"}, "external_identity": {"type": "string", "nullable": true, "description": "Custom identity attached via `Qonversion.identify()` in the SDK, if any.", "example": "user_42"}, "custom_uid": {"type": "string", "nullable": true, "description": "Custom user property `_q_custom_user_id` if set, falling back to the legacy `client_custom_uid` column.", "example": "user_42"}, "since": {"type": "integer", "format": "int64", "description": "First-seen timestamp (Unix seconds).", "example": 1699000000}, "last_payment_at": {"type": "integer", "format": "int64", "description": "Most recent paid-transaction timestamp (Unix seconds). 0 if the customer has never paid.", "example": 1701000000}, "first_transaction_purchase_at": {"type": "integer", "format": "int64", "description": "First paid-transaction timestamp (Unix seconds). 0 if no purchase has occurred.", "example": 1700000000}, "payments_count": {"type": "integer", "description": "Number of paid transactions.", "example": 3}, "purchase_currency": {"type": "string", "description": "Currency of the most recent purchase (ISO 4217), or empty string.", "example": "USD"}, "purchase_product_id": {"type": "string", "description": "Product ID of the most recent purchase.", "example": "premium_monthly"}, "net_payments_usd": {"type": "number", "format": "double", "description": "Net payments in USD (sales minus refunds).", "example": 49.99}, "status": {"type": "integer", "enum": [0, 1, 2, 3, 4], "description": "Subscription status:\n- 0 = Unknown / never had a tracked status event\n- 1 = Trial / Intro\n- 2 = Active\n- 3 = Canceled\n- 4 = Billing Retry\n", "example": 2}, "trial_status": {"type": "integer", "enum": [0, 1, 2, 3], "description": "Trial status:\n- 0 = No trial\n- 1 = Trial started\n- 2 = Trial converted\n- 3 = Trial canceled / expired\n", "example": 2}, "client_country": {"type": "string", "description": "Country code (ISO 3166-1 alpha-2).", "example": "US"}, "device_model": {"type": "string", "description": "Latest device model string.", "example": "iPhone15,2"}, "device_id": {"type": "string", "description": "Latest device ID.", "example": "ABCDEF12-3456"}, "target_platform": {"type": "string", "enum": ["iOS", "Android", "Stripe"], "description": "Latest platform.", "example": "iOS"}, "device_os_version": {"type": "string", "description": "Latest OS version string.", "example": "17.4"}}}, "CustomersListResponse": {"type": "object", "required": ["data"], "description": "Top-level envelope for `/customers`. Pagination is reported inside `data` as `total_count` and the echoed `page`; clients compute `total_pages` from `ceil(total_count / limit)`.", "properties": {"data": {"type": "object", "required": ["total_count", "items", "page"], "properties": {"total_count": {"type": "integer", "description": "Total number of customers matching the filters across all pages.", "example": 1542}, "items": {"type": "array", "description": "Customer entries for the current page.", "items": {"$ref": "#/components/schemas/Customer"}}, "page": {"type": "integer", "description": "Echoed 1-based page number from the request.", "example": 1}}}}}, "CustomerMetricsResponse": {"type": "object", "required": ["data"], "properties": {"data": {"$ref": "#/components/schemas/CustomerMetrics"}}}, "CustomerMetrics": {"type": "object", "description": "Aggregated customer metrics for the project, scoped to the requested filters.", "properties": {"clientsWithEvent": {"type": "integer", "description": "Total customers in the filtered set (rows produced by the customers index for these filters).", "example": 15420}, "activeSubscribers": {"type": "integer", "description": "Customers with `status = Active (2)` and at least one paid transaction.", "example": 4230}, "churnedSubscribers": {"type": "integer", "description": "Churn rate as a percentage (0-100), rounded to integer. Calculated as `canceled_with_payment / (active + canceled_with_payment)`.", "example": 43}, "activeTrials": {"type": "integer", "description": "Customers currently in a started trial that has not entered billing-failed state.", "example": 820}, "canceledTrials": {"type": "integer", "description": "Cancelled-trial rate as a percentage (0-100), rounded to integer. Calculated as `canceled_trials / total_trials`.", "example": 35}, "billingRetry": {"type": "integer", "description": "Customers in billing-retry state.", "example": 120}, "avgPaymentCount": {"type": "number", "format": "double", "description": "Average number of paid transactions per paying customer.", "example": 2.4}, "avgPrice": {"type": "number", "format": "double", "description": "Average net USD value per paid transaction.", "example": 9.95}, "sales": {"type": "number", "format": "double", "description": "Total net sales in USD across the filtered customers.", "example": 380276.24}}}}}}