Skip to content

Instantly share code, notes, and snippets.

@mysticaltech
Created June 11, 2025 03:01
Show Gist options
  • Save mysticaltech/85ac1c152f2385af31777075db7b001e to your computer and use it in GitHub Desktop.
Save mysticaltech/85ac1c152f2385af31777075db7b001e to your computer and use it in GitHub Desktop.
Profitwell v2 openapi.json
{
"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