Created
June 11, 2025 03:01
-
-
Save mysticaltech/85ac1c152f2385af31777075db7b001e to your computer and use it in GitHub Desktop.
Profitwell v2 openapi.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"openapi": "3.0.0", | |
"info": { | |
"title": "ProfitWell API v2", | |
"version": "v2", | |
"description": "The ProfitWell APIs allow you to get the most out of your account by programmatically adding and retrieving data.\n(Note: If you are looking for the v1 docs, you can find them [here](https://profitwell.com/docs/api/v1/))\n\n**Authentication**\nAuthentication is done via an API token that you can find by clicking the ProfitWell API data connection card in the Account Settings -> Integrations tab of your ProfitWell account. This token is unique to your company and should be kept secret.\nTo authenticate, set an `Authorization` request header equal to the value of your token on each request. Invalid or missing tokens will return a 401 Unauthorized response.\n- Your **private token** is required for all ProfitWell API endpoints except for the Customer Events endpoint.\n- Your **public token** is required for the Customer Events API endpoint, as well as authentication with the Profitwell.js script.\n\n**Request Rate Limit**\nThe ProfitWell API employs several safeguards against bursts of incoming traffic to help maximize its stability. If a user sends many requests in quick succession they may see error responses that show up as \"Request was throttled.\" (HTTP 429).\n- All API requests, regardless of endpoint, have a base limit of 25 requests every 5 seconds. That is to say, an average limit of 5 requests per second, with bursts of up to 25.\n- Plan updates (list, create, retrieve, update plans): 1000/day.\n- Metrics updates (monthly and daily metrics or company settings): 3000/day.\n- Customer exclusions: 10/minute.\n- Anonymization requests: 20/minute.\n- Customer traits requests: 20/minute." | |
}, | |
"servers": [ | |
{ | |
"url": "https://api.profitwell.com/v2", | |
"description": "Main ProfitWell API v2 server" | |
}, | |
{ | |
"url": "https://api.profitwell-events.com/dotjs/v1", | |
"description": "ProfitWell Customer Events API server" | |
} | |
], | |
"components": { | |
"securitySchemes": { | |
"privateToken": { | |
"type": "apiKey", | |
"in": "header", | |
"name": "Authorization", | |
"description": "Your private token, required for most ProfitWell API v2 endpoints." | |
}, | |
"publicToken": { | |
"type": "apiKey", | |
"in": "header", | |
"name": "Authorization", | |
"description": "Your public token, required for the Customer Events API endpoint." | |
} | |
}, | |
"schemas": { | |
"SubscriptionCreateRequest": { | |
"type": "object", | |
"properties": { | |
"user_id": { | |
"type": "string", | |
"description": "The ProfitWell-generated user ID. Do not include for a user's first subscription. Only include if referencing an existing user for an additional subscription." | |
}, | |
"user_alias": { | |
"type": "string", | |
"description": "Your own identifier for the user. Useful if you don't store ProfitWell user_ids. Required if creating a second subscription for a user and not using user_id." | |
}, | |
"subscription_alias": { | |
"type": "string", | |
"maxLength": 36, | |
"description": "Your own identifier for this subscription. Must be unique across all users in your company. Max 36 characters." | |
}, | |
"email": { | |
"type": "string", | |
"description": "Email address or display text for the user on the Customers tab. Does not uniquely identify a user." | |
}, | |
"plan_id": { | |
"type": "string", | |
"description": "The ID of the plan. Should be consistent for segmentation." | |
}, | |
"plan_interval": { | |
"type": "string", | |
"enum": ["month", "year"], | |
"description": "Billing cycle for the plan." | |
}, | |
"plan_currency": { | |
"type": "string", | |
"description": "ISO 4217 currency code (e.g., \"usd\", \"eur\"). Defaults to \"usd\".", | |
"default": "usd" | |
}, | |
"status": { | |
"type": "string", | |
"enum": ["active", "trialing"], | |
"description": "Status of the subscription. Defaults to \"active\". Trialing subscriptions must have value 0.", | |
"default": "active" | |
}, | |
"value": { | |
"type": "integer", | |
"format": "int64", | |
"description": "Amount billed per period, in cents (e.g., $120.00/year plan is 12000). Trialing subscriptions must have value 0." | |
}, | |
"effective_date": { | |
"type": "integer", | |
"format": "int64", | |
"description": "Subscription start date, UNIX timestamp (e.g., 2018-01-01 is 1514764800)." | |
}, | |
"data_provider_user_id": { | |
"type": "string", | |
"description": "(Currently not used) Data provider's user ID (e.g., Stripe User ID like cus_A75fBhseE80) to associate with a manually-added user." | |
} | |
}, | |
"required": [ | |
"email", | |
"plan_id", | |
"plan_interval", | |
"value", | |
"effective_date" | |
] | |
}, | |
"SubscriptionResponse": { | |
"type": "object", | |
"properties": { | |
"subscription_id": { | |
"type": "string", | |
"description": "The ProfitWell-generated ID for the subscription." | |
}, | |
"user_id": { | |
"type": "string", | |
"description": "The ProfitWell-generated ID for the user." | |
}, | |
"user_alias": { | |
"type": "string", | |
"nullable": true | |
}, | |
"subscription_alias": { | |
"type": "string", | |
"nullable": true | |
}, | |
"email": { | |
"type": "string" | |
}, | |
"plan_id": { | |
"type": "string" | |
}, | |
"plan_interval": { | |
"type": "string" | |
}, | |
"plan_currency": { | |
"type": "string" | |
}, | |
"status": { | |
"type": "string" | |
}, | |
"value": { | |
"type": "integer", | |
"format": "int64" | |
}, | |
"effective_date": { | |
"type": "integer", | |
"format": "int64" | |
} | |
}, | |
"required": [ | |
"subscription_id", | |
"user_id", | |
"email", | |
"plan_id", | |
"plan_interval", | |
"status", | |
"value", | |
"effective_date" | |
] | |
}, | |
"SubscriptionUpdateRequest": { | |
"type": "object", | |
"properties": { | |
"plan_id": { | |
"type": "string", | |
"description": "The ID of the new plan. Use the same plan_id if only seats changed." | |
}, | |
"plan_interval": { | |
"type": "string", | |
"enum": ["month", "year"], | |
"description": "Billing cycle for this plan." | |
}, | |
"value": { | |
"type": "integer", | |
"format": "int64", | |
"description": "New amount billed per period, in cents. Full value, even if prorated." | |
}, | |
"status": { | |
"type": "string", | |
"enum": ["active"], | |
"description": "Subscription status. Currently only \"active\" is valid for upgrades/downgrades.", | |
"default": "active" | |
}, | |
"effective_date": { | |
"type": "integer", | |
"format": "int64", | |
"description": "Date this update takes effect, UNIX timestamp." | |
} | |
}, | |
"required": [ | |
"plan_id", | |
"plan_interval", | |
"value", | |
"effective_date" | |
] | |
}, | |
"UserSubscriptionHistoryItem": { | |
"type": "object", | |
"properties": { | |
"subscription_id": { | |
"type": "string" | |
}, | |
"plan_id": { | |
"type": "string" | |
}, | |
"plan_interval": { | |
"type": "string" | |
}, | |
"value": { | |
"type": "integer", | |
"format": "int64" | |
}, | |
"status": { | |
"type": "string", | |
"description": "Can be 'active', 'trialing', 'churned_voluntary', 'churned_delinquent', etc." | |
}, | |
"effective_date": { | |
"type": "integer", | |
"format": "int64" | |
} | |
} | |
}, | |
"UserSubscriptionHistoryResponse": { | |
"type": "array", | |
"items": { | |
"$ref": "#/components/schemas/UserSubscriptionHistoryItem" | |
} | |
}, | |
"UserUpdateRequest": { | |
"type": "object", | |
"properties": { | |
"email": { | |
"type": "string", | |
"description": "New email address or display text for the user." | |
} | |
}, | |
"required": [ | |
"email" | |
] | |
}, | |
"Plan": { | |
"type": "object", | |
"properties": { | |
"id": { | |
"type": "string", | |
"description": "The ID of the plan." | |
}, | |
"name": { | |
"type": "string", | |
"description": "The name of the plan." | |
} | |
}, | |
"required": [ | |
"id", | |
"name" | |
] | |
}, | |
"PlanCreateRequest": { | |
"type": "object", | |
"properties": { | |
"id": { | |
"type": "string", | |
"description": "The ID of the new plan. Must not conflict with existing plans." | |
}, | |
"name": { | |
"type": "string", | |
"description": "The name of the plan." | |
} | |
}, | |
"required": [ | |
"id", | |
"name" | |
] | |
}, | |
"PlanUpdateRequest": { | |
"type": "object", | |
"properties": { | |
"name": { | |
"type": "string", | |
"description": "The new name of the plan." | |
} | |
}, | |
"required": [ | |
"name" | |
] | |
}, | |
"Customer": { | |
"type": "object", | |
"properties": { | |
"customer_id": { | |
"type": "string", | |
"description": "The data-provider specific customer ID." | |
}, | |
"email": { | |
"type": "string", | |
"nullable": true | |
} | |
}, | |
"additionalProperties": true | |
}, | |
"CustomerList": { | |
"type": "array", | |
"items": { | |
"$ref": "#/components/schemas/Customer" | |
} | |
}, | |
"MetricDataPoint": { | |
"type": "object", | |
"properties": { | |
"date": { | |
"type": "string", | |
"description": "Date of the metric (YYYY-MM for monthly, YYYY-MM-DD for daily)." | |
}, | |
"value": { | |
"type": "number", | |
"format": "double", | |
"nullable": true, | |
"description": "Value of the metric. Can be null (e.g., growth rate from $0 MRR)." | |
} | |
}, | |
"required": [ | |
"date", | |
"value" | |
] | |
}, | |
"MetricsResponseData": { | |
"type": "object", | |
"description": "Keys are metric trend names, values are arrays of data points.", | |
"additionalProperties": { | |
"type": "array", | |
"items": { | |
"$ref": "#/components/schemas/MetricDataPoint" | |
} | |
} | |
}, | |
"MetricsResponse": { | |
"type": "object", | |
"properties": { | |
"data": { | |
"$ref": "#/components/schemas/MetricsResponseData" | |
} | |
}, | |
"required": [ | |
"data" | |
] | |
}, | |
"PlanIdList": { | |
"type": "array", | |
"items": { | |
"type": "string", | |
"description": "An active plan ID." | |
} | |
}, | |
"StopRetainRequest": { | |
"type": "object", | |
"properties": { | |
"customer_id": { | |
"type": "string", | |
"description": "The customer ID." | |
}, | |
"intervention_types": { | |
"type": "array", | |
"items": { | |
"type": "string", | |
"enum": ["retain", "term_optimization", "reactivation"] | |
}, | |
"description": "List of interventions to stop for the customer." | |
}, | |
"forever": { | |
"type": "boolean", | |
"description": "Indicates whether to exclude the user from interventions indefinitely. Default is false.", | |
"default": false | |
} | |
}, | |
"required": [ | |
"customer_id", | |
"intervention_types" | |
] | |
}, | |
"CompanySettings": { | |
"type": "object", | |
"properties": { | |
"company_id": { | |
"type": "string" | |
}, | |
"name": { | |
"type": "string" | |
}, | |
"timezone": { | |
"type": "string" | |
}, | |
"currency": { | |
"type": "string", | |
"description": "ISO 4217 currency code in which metrics are displayed." | |
} | |
} | |
}, | |
"TraitRequest": { | |
"type": "object", | |
"properties": { | |
"customer_id": { | |
"type": "string", | |
"description": "The data-provider specific customer ID. One of customer_id or email is required." | |
}, | |
"email": { | |
"type": "string", | |
"description": "The customer's email address. One of customer_id or email is required." | |
}, | |
"category": { | |
"type": "string", | |
"description": "The category of the trait (case-insensitive, stored as lower-case). Cannot be a reserved category name." | |
}, | |
"trait": { | |
"type": "string", | |
"description": "The value of the trait (case-insensitive, stored as lower-case). Cannot be blank, None, or #N/A." | |
} | |
}, | |
"required": [ | |
"category", | |
"trait" | |
] | |
}, | |
"TraitRemoveRequest": { | |
"type": "object", | |
"properties": { | |
"customer_id": { | |
"type": "string", | |
"description": "The data-provider specific customer ID. One of customer_id or email is required." | |
}, | |
"email": { | |
"type": "string", | |
"description": "The customer's email address. One of customer_id or email is required." | |
}, | |
"category": { | |
"type": "string", | |
"description": "The category of the trait to remove." | |
} | |
}, | |
"required": [ | |
"category" | |
] | |
}, | |
"CustomerTraitsResponse": { | |
"type": "object", | |
"description": "A map of category to trait for the customer.", | |
"additionalProperties": { | |
"type": "string" | |
}, | |
"example": { | |
"super_power": "gadgets", | |
"location": "gotham" | |
} | |
}, | |
"CustomerEventRequest": { | |
"type": "object", | |
"properties": { | |
"user_id": { | |
"type": "string", | |
"description": "The user ID (e.g., cus_1234) of the customer who logged in." | |
} | |
}, | |
"required": [ | |
"user_id" | |
] | |
}, | |
"ErrorResponse": { | |
"type": "object", | |
"properties": { | |
"error": { | |
"type": "string", | |
"description": "A message describing the error." | |
} | |
} | |
}, | |
"DateStringOrTimestampSchema": { | |
"type": "string", | |
"description": "A date/time value that can be provided as a Unix timestamp (integer) or a string in various common date formats (e.g., ISO 8601 'YYYY-MM-DDTHH:mm:ssZ')." | |
} | |
}, | |
"parameters": { | |
"SubscriptionIdentifier": { | |
"name": "subscription_id_OR_subscription_alias", | |
"in": "path", | |
"required": true, | |
"description": "Either the ProfitWell-generated subscription_id or your subscription_alias.", | |
"schema": { | |
"type": "string" | |
} | |
}, | |
"UserIdentifier": { | |
"name": "user_id_OR_user_alias", | |
"in": "path", | |
"required": true, | |
"description": "Either the ProfitWell-generated user_id or your user_alias.", | |
"schema": { | |
"type": "string" | |
} | |
}, | |
"PlanIdPath": { | |
"name": "id", | |
"in": "path", | |
"required": true, | |
"description": "The ID of the manually-added plan.", | |
"schema": { | |
"type": "string" | |
} | |
}, | |
"CustomerIdPath": { | |
"name": "customer_id", | |
"in": "path", | |
"required": true, | |
"description": "The data-provider specific customer ID.", | |
"schema": { | |
"type": "string" | |
} | |
}, | |
"PageQuery": { | |
"name": "page", | |
"in": "query", | |
"description": "The page number. Max value depends on per_page (100,000 / per_page). Default 1.", | |
"schema": { | |
"type": "integer", | |
"default": 1 | |
} | |
}, | |
"PerPageQuery": { | |
"name": "per_page", | |
"in": "query", | |
"description": "The number of items per page. Max value 250. Default 250.", | |
"schema": { | |
"type": "integer", | |
"maximum": 250, | |
"default": 250 | |
} | |
} | |
}, | |
"responses": { | |
"Unauthorized": { | |
"description": "Unauthorized. Invalid or missing API token.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
}, | |
"example": { | |
"error": "Unauthorized" | |
} | |
} | |
} | |
}, | |
"BadRequest": { | |
"description": "Bad Request. The request was invalid. See error message for details.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"NotFound": { | |
"description": "Not Found. The requested resource could not be found.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"Throttled": { | |
"description": "Request was throttled due to rate limiting.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
}, | |
"example": { | |
"error": "Request was throttled." | |
} | |
} | |
} | |
}, | |
"UnsupportedMediaType": { | |
"description": "Unsupported Media Type. E.g., Content-Type header is not application/json for an endpoint that requires it.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
} | |
} | |
}, | |
"security": [ | |
{ | |
"privateToken": [] | |
} | |
], | |
"tags": [ | |
{ | |
"name": "API Status", | |
"description": "Check the operational status of the API." | |
}, | |
{ | |
"name": "Manually-Added Subscriptions", | |
"description": "Manage manually-added subscriptions." | |
}, | |
{ | |
"name": "Users", | |
"description": "Manage user information and their subscription history." | |
}, | |
{ | |
"name": "Plans", | |
"description": "Manage manually-added plans." | |
}, | |
{ | |
"name": "Customers", | |
"description": "Retrieve customer data." | |
}, | |
{ | |
"name": "GDPR", | |
"description": "Anonymize customer data in compliance with GDPR." | |
}, | |
{ | |
"name": "Metrics", | |
"description": "Access financial metrics and plan data." | |
}, | |
{ | |
"name": "Retain", | |
"description": "Manage ProfitWell Retain interventions." | |
}, | |
{ | |
"name": "Company", | |
"description": "Retrieve company account settings." | |
}, | |
{ | |
"name": "Customer Traits", | |
"description": "Manage custom traits for customers." | |
}, | |
{ | |
"name": "Engagement", | |
"description": "Track customer engagement events." | |
} | |
], | |
"paths": { | |
"/status": { | |
"get": { | |
"tags": ["API Status"], | |
"summary": "Get API Status", | |
"description": "Returns a status code of 200 if the API is operational and authentication is successful. Returns 401 if authentication fails.", | |
"operationId": "getApiStatus", | |
"responses": { | |
"200": { | |
"description": "API is operational and authentication successful." | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/subscriptions": { | |
"post": { | |
"tags": ["Manually-Added Subscriptions"], | |
"summary": "Create a new subscription", | |
"description": "Create a new subscription for a new or existing user.\nStore either the `subscription_alias` you provide or the `subscription_id` returned by ProfitWell for future updates.\nIf creating multiple subscriptions for the same user, wait for the API response from the first creation before creating subsequent ones.", | |
"operationId": "createSubscription", | |
"requestBody": { | |
"required": true, | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/SubscriptionCreateRequest" | |
} | |
} | |
} | |
}, | |
"responses": { | |
"201": { | |
"description": "Subscription created successfully.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/SubscriptionResponse" | |
} | |
} | |
} | |
}, | |
"400": { | |
"$ref": "#/components/responses/BadRequest" | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/subscriptions/{subscription_id_OR_subscription_alias}": { | |
"put": { | |
"tags": ["Manually-Added Subscriptions"], | |
"summary": "Upgrade/Downgrade a subscription", | |
"description": "Upgrade or downgrade an existing subscription's plan, value, or status.", | |
"operationId": "updateSubscription", | |
"parameters": [ | |
{ | |
"$ref": "#/components/parameters/SubscriptionIdentifier" | |
} | |
], | |
"requestBody": { | |
"required": true, | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/SubscriptionUpdateRequest" | |
} | |
} | |
} | |
}, | |
"responses": { | |
"200": { | |
"description": "Subscription updated successfully." | |
}, | |
"400": { | |
"$ref": "#/components/responses/BadRequest" | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"$ref": "#/components/responses/NotFound" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/subscriptions/{subscription_id_OR_subscription_alias}/churn": { | |
"post": { | |
"tags": ["Manually-Added Subscriptions"], | |
"summary": "Churn a subscription", | |
"description": "Mark an existing subscription as churned.", | |
"operationId": "churnSubscription", | |
"parameters": [ | |
{ | |
"$ref": "#/components/parameters/SubscriptionIdentifier" | |
}, | |
{ | |
"name": "effective_date", | |
"in": "query", | |
"required": true, | |
"description": "UNIX timestamp of when the subscription churns.", | |
"schema": { | |
"type": "integer", | |
"format": "int64" | |
} | |
}, | |
{ | |
"name": "churn_type", | |
"in": "query", | |
"description": "Type of churn. Defaults to 'voluntary'.", | |
"schema": { | |
"type": "string", | |
"enum": ["voluntary", "delinquent"], | |
"default": "voluntary" | |
} | |
} | |
], | |
"responses": { | |
"200": { | |
"description": "Subscription churned successfully." | |
}, | |
"400": { | |
"$ref": "#/components/responses/BadRequest" | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"$ref": "#/components/responses/NotFound" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/subscriptions/{subscription_id_OR_subscription_alias}/unchurn": { | |
"post": { | |
"tags": ["Manually-Added Subscriptions"], | |
"summary": "Un-churn a subscription", | |
"description": "Remove the churn event associated with a subscription, making it appear as though it never churned.", | |
"operationId": "unchurnSubscription", | |
"parameters": [ | |
{ | |
"$ref": "#/components/parameters/SubscriptionIdentifier" | |
} | |
], | |
"responses": { | |
"204": { | |
"description": "Subscription un-churned successfully. No content." | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"$ref": "#/components/responses/NotFound" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/users/{user_id_OR_user_alias}/subscription_history": { | |
"get": { | |
"tags": ["Users"], | |
"summary": "Get subscription history for a user", | |
"description": "Retrieve the history of subscription updates made for a specific user.", | |
"operationId": "getUserSubscriptionHistory", | |
"parameters": [ | |
{ | |
"$ref": "#/components/parameters/UserIdentifier" | |
} | |
], | |
"responses": { | |
"200": { | |
"description": "Successfully retrieved subscription history.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/UserSubscriptionHistoryResponse" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"$ref": "#/components/responses/NotFound" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/users/{user_id_OR_user_alias}": { | |
"put": { | |
"tags": ["Users"], | |
"summary": "Update a user's email", | |
"description": "Update the email address (or display text) for a user.", | |
"operationId": "updateUser", | |
"parameters": [ | |
{ | |
"$ref": "#/components/parameters/UserIdentifier" | |
} | |
], | |
"requestBody": { | |
"required": true, | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/UserUpdateRequest" | |
} | |
} | |
} | |
}, | |
"responses": { | |
"204": { | |
"description": "User updated successfully. No content." | |
}, | |
"400": { | |
"$ref": "#/components/responses/BadRequest" | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"$ref": "#/components/responses/NotFound" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
}, | |
"delete": { | |
"tags": ["Users"], | |
"summary": "Delete a user", | |
"description": "Completely delete a user and all their subscription history. This action is irreversible.", | |
"operationId": "deleteUser", | |
"parameters": [ | |
{ | |
"$ref": "#/components/parameters/UserIdentifier" | |
} | |
], | |
"responses": { | |
"204": { | |
"description": "User deleted successfully. No content." | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"$ref": "#/components/responses/NotFound" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/plans": { | |
"get": { | |
"tags": ["Plans"], | |
"summary": "List all manually-added plans", | |
"description": "Retrieve a list of all plans that were added manually.", | |
"operationId": "listManuallyAddedPlans", | |
"responses": { | |
"200": { | |
"description": "Successfully retrieved list of plans.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"type": "array", | |
"items": { | |
"$ref": "#/components/schemas/Plan" | |
} | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
}, | |
"post": { | |
"tags": ["Plans"], | |
"summary": "Create a new manually-added plan", | |
"description": "Create a new plan. Alternatively, a plan is automatically created if a manually-added subscription references a new plan ID.\nReturns an error if a plan with the given ID already exists.", | |
"operationId": "createManuallyAddedPlan", | |
"requestBody": { | |
"required": true, | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/PlanCreateRequest" | |
} | |
} | |
} | |
}, | |
"responses": { | |
"201": { | |
"description": "Plan created successfully.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/Plan" | |
} | |
} | |
} | |
}, | |
"400": { | |
"description": "Plan already exists or invalid request.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/plans/{id}": { | |
"get": { | |
"tags": ["Plans"], | |
"summary": "Retrieve a manually-added plan", | |
"description": "Retrieve a single manually-added plan by its ID.", | |
"operationId": "getManuallyAddedPlan", | |
"parameters": [ | |
{ | |
"$ref": "#/components/parameters/PlanIdPath" | |
} | |
], | |
"responses": { | |
"200": { | |
"description": "Successfully retrieved plan.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/Plan" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"$ref": "#/components/responses/NotFound" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
}, | |
"put": { | |
"tags": ["Plans"], | |
"summary": "Update a manually-added plan", | |
"description": "Update the name of an existing manually-added plan.", | |
"operationId": "updateManuallyAddedPlan", | |
"parameters": [ | |
{ | |
"$ref": "#/components/parameters/PlanIdPath" | |
} | |
], | |
"requestBody": { | |
"required": true, | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/PlanUpdateRequest" | |
} | |
} | |
} | |
}, | |
"responses": { | |
"200": { | |
"description": "Plan updated successfully.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/Plan" | |
} | |
} | |
} | |
}, | |
"400": { | |
"$ref": "#/components/responses/BadRequest" | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"$ref": "#/components/responses/NotFound" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/customers/{customer_id}": { | |
"get": { | |
"tags": ["Customers"], | |
"summary": "Retrieve a customer by ID", | |
"description": "Retrieve an individual customer by their data-provider specific customer ID.", | |
"operationId": "getCustomerById", | |
"parameters": [ | |
{ | |
"$ref": "#/components/parameters/CustomerIdPath" | |
} | |
], | |
"responses": { | |
"200": { | |
"description": "Successfully retrieved customer.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/Customer" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"$ref": "#/components/responses/NotFound" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/customers": { | |
"get": { | |
"tags": ["Customers"], | |
"summary": "Search for customers", | |
"description": "Search for customers by email or iterate through customers updated within a date range.\nWhen iterating with `start_date`, be mindful of the `page` limit (100,000 / `per_page`). Move `start_date` forward for better performance with large datasets.", | |
"operationId": "searchCustomers", | |
"parameters": [ | |
{ | |
"name": "start_date", | |
"in": "query", | |
"description": "Get customers updated on or after this date.", | |
"required": false, | |
"schema": { | |
"$ref": "#/components/schemas/DateStringOrTimestampSchema" | |
} | |
}, | |
{ | |
"name": "end_date", | |
"in": "query", | |
"description": "Get customers updated before this date.", | |
"required": false, | |
"schema": { | |
"$ref": "#/components/schemas/DateStringOrTimestampSchema" | |
} | |
}, | |
{ | |
"name": "email", | |
"in": "query", | |
"description": "The customer's email address to search for.", | |
"schema": { | |
"type": "string" | |
} | |
}, | |
{ | |
"$ref": "#/components/parameters/PageQuery" | |
}, | |
{ | |
"$ref": "#/components/parameters/PerPageQuery" | |
}, | |
{ | |
"name": "direction", | |
"in": "query", | |
"description": "Order data by 'asc' (ascending) or 'desc' (descending). Default 'asc'.", | |
"schema": { | |
"type": "string", | |
"enum": ["asc", "desc"], | |
"default": "asc" | |
} | |
} | |
], | |
"responses": { | |
"200": { | |
"description": "Successfully retrieved list of customers.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/CustomerList" | |
} | |
} | |
} | |
}, | |
"400": { | |
"$ref": "#/components/responses/BadRequest" | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/customers/{customer_id}/anonymize": { | |
"post": { | |
"tags": ["GDPR"], | |
"summary": "Anonymize a customer by ID", | |
"description": "Anonymize a customer's data using their data-provider specific customer ID.", | |
"operationId": "anonymizeCustomerById", | |
"parameters": [ | |
{ | |
"$ref": "#/components/parameters/CustomerIdPath" | |
} | |
], | |
"responses": { | |
"204": { | |
"description": "Customer anonymized successfully. No content." | |
}, | |
"400": { | |
"description": "Customer has already been anonymized.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"$ref": "#/components/responses/NotFound" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/customers/anonymize_by_email": { | |
"post": { | |
"tags": ["GDPR"], | |
"summary": "Anonymize a customer by email", | |
"description": "Anonymize a customer's data using their email address.", | |
"operationId": "anonymizeCustomerByEmail", | |
"parameters": [ | |
{ | |
"name": "email", | |
"in": "query", | |
"required": true, | |
"description": "The email address of the customer to anonymize.", | |
"schema": { | |
"type": "string" | |
} | |
} | |
], | |
"responses": { | |
"204": { | |
"description": "Customer (potentially) anonymized successfully. No content." | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"description": "Customer not found or already anonymized (indistinguishable).", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/metrics/monthly": { | |
"get": { | |
"tags": ["Metrics"], | |
"summary": "Get monthly metrics", | |
"description": "Retrieve all monthly financial metrics for your company. Optionally scope to an individual metric and/or plan.\nResponse `data` object has metric names as keys, and arrays of `{date, value}` records as values.\nDates are YYYY-MM. Values are numbers (currency amounts in account settings currency, rates as percentages e.g., 8.3 for 8.3%).\nRefer to API documentation for available metrics and their applicability (All Plans vs Individual Plan).", | |
"operationId": "getMonthlyMetrics", | |
"parameters": [ | |
{ | |
"name": "plan_id", | |
"in": "query", | |
"description": "Optionally filter metrics for a specific plan_id.", | |
"schema": { | |
"type": "string" | |
} | |
}, | |
{ | |
"name": "metrics", | |
"in": "query", | |
"description": "Optional, comma-separated list of metric trend names to return (e.g., \"recurring_revenue,active_customers\"). Defaults to all metrics.", | |
"schema": { | |
"type": "string" | |
} | |
} | |
], | |
"responses": { | |
"200": { | |
"description": "Successfully retrieved monthly metrics.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/MetricsResponse" | |
} | |
} | |
} | |
}, | |
"400": { | |
"description": "Invalid metrics trend names or unknown query parameters.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/metrics/daily": { | |
"get": { | |
"tags": ["Metrics"], | |
"summary": "Get daily metrics", | |
"description": "Retrieve financial metrics broken down by day for the current or previous month. Optionally scope to an individual metric and/or plan.\nResponse `data` object has metric names as keys, and arrays of `{date, value}` records as values.\nDates are YYYY-MM-DD. Values are numbers (currency amounts in account settings currency).\nRefer to API documentation for available metrics and their segmentability by plan.", | |
"operationId": "getDailyMetrics", | |
"parameters": [ | |
{ | |
"name": "month", | |
"in": "query", | |
"required": true, | |
"description": "Month for daily metrics (YYYY-MM, e.g., \"2018-09\"). Current or previous month only.", | |
"schema": { | |
"type": "string", | |
"pattern": "^\\d{4}-\\d{2}$" | |
} | |
}, | |
{ | |
"name": "plan_id", | |
"in": "query", | |
"description": "Optionally filter metrics for a specific plan_id.", | |
"schema": { | |
"type": "string" | |
} | |
}, | |
{ | |
"name": "metrics", | |
"in": "query", | |
"description": "Optional, comma-separated list of metric trend names to return. Defaults to all metrics.", | |
"schema": { | |
"type": "string" | |
} | |
} | |
], | |
"responses": { | |
"200": { | |
"description": "Successfully retrieved daily metrics.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/MetricsResponse" | |
} | |
} | |
} | |
}, | |
"400": { | |
"description": "Invalid month, metrics trend names, or unknown query parameters.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/plan_ids": { | |
"get": { | |
"tags": ["Metrics"], | |
"summary": "Get active plan IDs", | |
"description": "Retrieve your company's active plan IDs, sorted by MRR. Returns plan IDs with currently active customers, up to 150.", | |
"operationId": "getActivePlanIds", | |
"parameters": [ | |
{ | |
"name": "limit", | |
"in": "query", | |
"description": "Optionally limit the number of plan IDs returned (max and default 150).", | |
"schema": { | |
"type": "integer", | |
"maximum": 150, | |
"default": 150 | |
} | |
} | |
], | |
"responses": { | |
"200": { | |
"description": "Successfully retrieved active plan IDs.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/PlanIdList" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/customers/{customer_id}/exclude": { | |
"post": { | |
"tags": ["Metrics"], | |
"summary": "Exclude customer from metrics", | |
"description": "Exclude a user's data from the calculation of all metrics.", | |
"operationId": "excludeCustomerFromMetrics", | |
"parameters": [ | |
{ | |
"$ref": "#/components/parameters/CustomerIdPath" | |
} | |
], | |
"responses": { | |
"204": { | |
"description": "Customer excluded successfully. No content." | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"$ref": "#/components/responses/NotFound" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/retain/stop_interventions": { | |
"post": { | |
"tags": ["Retain"], | |
"summary": "Stop Retain interventions for a customer", | |
"description": "Stop specified Retain interventions (emails, retries, notifications) for a given customer.", | |
"operationId": "stopRetainInterventions", | |
"requestBody": { | |
"required": true, | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/StopRetainRequest" | |
} | |
} | |
} | |
}, | |
"responses": { | |
"204": { | |
"description": "Retain interventions stopped successfully. No content." | |
}, | |
"400": { | |
"description": "No open payment for the customer or invalid request.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/retain/unsubscribed_customers": { | |
"get": { | |
"tags": ["Retain"], | |
"summary": "Get customers unsubscribed from Retain emails", | |
"description": "Retrieve customers who unsubscribed from Retain Term Optimization or Reactivation emails.", | |
"operationId": "getRetainUnsubscribedCustomers", | |
"parameters": [ | |
{ | |
"name": "intervention_type", | |
"in": "query", | |
"required": true, | |
"description": "Must be \"term_optimization\" or \"reactivation\".", | |
"schema": { | |
"type": "string", | |
"enum": ["term_optimization", "reactivation"] | |
} | |
}, | |
{ | |
"name": "start_date", | |
"in": "query", | |
"description": "Get customers unsubscribed on or after this date.", | |
"required": false, | |
"schema": { | |
"$ref": "#/components/schemas/DateStringOrTimestampSchema" | |
} | |
}, | |
{ | |
"name": "end_date", | |
"in": "query", | |
"description": "Get customers unsubscribed on or before this date.", | |
"required": false, | |
"schema": { | |
"$ref": "#/components/schemas/DateStringOrTimestampSchema" | |
} | |
}, | |
{ | |
"$ref": "#/components/parameters/PageQuery" | |
}, | |
{ | |
"$ref": "#/components/parameters/PerPageQuery" | |
} | |
], | |
"responses": { | |
"200": { | |
"description": "Successfully retrieved list of unsubscribed customers.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/CustomerList" | |
} | |
} | |
} | |
}, | |
"400": { | |
"$ref": "#/components/responses/BadRequest" | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/company/settings": { | |
"get": { | |
"tags": ["Company"], | |
"summary": "Get company account settings", | |
"description": "Retrieve your company's ProfitWell account settings (ID, name, timezone, display currency).", | |
"operationId": "getCompanySettings", | |
"responses": { | |
"200": { | |
"description": "Successfully retrieved company settings.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/CompanySettings" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/traits": { | |
"post": { | |
"tags": ["Customer Traits"], | |
"summary": "Create or update a customer trait", | |
"description": "Create a new trait or update an existing trait for a customer, identified by `customer_id` or `email`.\nEach customer can have one trait per category. A subsequent request for the same customer and category updates the trait.\nCustomer traits are case-insensitive. Reserved category names cannot be used.\nBlank, None, #N/A values for category or trait are invalid.\nCategories can have at most 100 unique traits.\nIf customers are unlabeled for a category, ProfitWell extrapolates data; assign \"n/a\" if extrapolation is not desired.", | |
"operationId": "createOrUpdateCustomerTrait", | |
"requestBody": { | |
"required": true, | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/TraitRequest" | |
} | |
} | |
} | |
}, | |
"responses": { | |
"204": { | |
"description": "Trait created or updated successfully. No content." | |
}, | |
"400": { | |
"description": "Invalid request (e.g., blank values, reserved category, adding existing trait, validation error).", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/traits/remove": { | |
"post": { | |
"tags": ["Customer Traits"], | |
"summary": "Remove a trait from a customer", | |
"description": "Remove a specific trait category from a customer.", | |
"operationId": "removeCustomerTrait", | |
"requestBody": { | |
"required": true, | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/TraitRemoveRequest" | |
} | |
} | |
} | |
}, | |
"responses": { | |
"204": { | |
"description": "Trait removed successfully. No content." | |
}, | |
"400": { | |
"description": "Invalid request (e.g., customer does not have the trait).", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"description": "Customer not found.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/customers/{customerIdentifier}/traits": { | |
"get": { | |
"tags": ["Customer Traits"], | |
"summary": "Get customer's traits", | |
"description": "Retrieve all traits for a specific customer. The customer is identified by a path parameter (e.g., customer ID).", | |
"operationId": "getCustomerTraits", | |
"parameters": [ | |
{ | |
"name": "customerIdentifier", | |
"in": "path", | |
"required": true, | |
"description": "Identifier for the customer (e.g., ProfitWell user_id, user_alias, or data-provider customer_id).", | |
"schema": { | |
"type": "string" | |
} | |
} | |
], | |
"responses": { | |
"200": { | |
"description": "Successfully retrieved customer traits.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/CustomerTraitsResponse" | |
} | |
} | |
} | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"$ref": "#/components/responses/NotFound" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/traits/categories/{category}": { | |
"delete": { | |
"tags": ["Customer Traits"], | |
"summary": "Remove a trait category", | |
"description": "Remove a trait category entirely. This removes it from every customer that has a trait for this category.\nThis action is irreversible.", | |
"operationId": "removeTraitCategory", | |
"parameters": [ | |
{ | |
"name": "category", | |
"in": "path", | |
"required": true, | |
"description": "The name of the category to remove (case-insensitive).", | |
"schema": { | |
"type": "string" | |
} | |
} | |
], | |
"responses": { | |
"204": { | |
"description": "Category removed successfully. No content." | |
}, | |
"401": { | |
"$ref": "#/components/responses/Unauthorized" | |
}, | |
"404": { | |
"description": "Category not found.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
}, | |
"/customer/event/": { | |
"post": { | |
"servers": [ | |
{ | |
"url": "https://api.profitwell-events.com/dotjs/v1" | |
} | |
], | |
"tags": ["Engagement"], | |
"summary": "Create a customer event", | |
"description": "Create a customer event, typically when a customer logs into your system.\nUse this if integrating profitwell.js or Paddle.js is not possible.\n**Requires your public API token for authentication.**", | |
"operationId": "createCustomerEvent", | |
"security": [ | |
{ | |
"publicToken": [] | |
} | |
], | |
"requestBody": { | |
"required": true, | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/CustomerEventRequest" | |
} | |
} | |
} | |
}, | |
"responses": { | |
"204": { | |
"description": "Customer event created successfully. No content." | |
}, | |
"400": { | |
"description": "Invalid JSON body or customer cannot be found.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"401": { | |
"description": "Unauthorized. Invalid or missing public API token.", | |
"content": { | |
"application/json": { | |
"schema": { | |
"$ref": "#/components/schemas/ErrorResponse" | |
} | |
} | |
} | |
}, | |
"415": { | |
"$ref": "#/components/responses/UnsupportedMediaType" | |
}, | |
"429": { | |
"$ref": "#/components/responses/Throttled" | |
} | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment