Skip to main content
POST
/
api
/
v1
/
pricing
/
analysis
Subscription Enumeration
curl --request POST \
  --url https://api.example.com/api/v1/pricing/analysis \
  --header 'Content-Type: application/json' \
  --data '
{
  "operation": "<string>",
  "solver": "<string>",
  "filters": "<string>"
}
'
{
  "jobId": "<string>",
  "status": "<string>",
  "submittedAt": "<string>",
  "startedAt": "<string>",
  "completedAt": "<string>",
  "result": {
    "result.subscriptions": [
      {
        "result.subscriptions[].plan": "<string>",
        "result.subscriptions[].addOns": [
          "<string>"
        ],
        "result.subscriptions[].features": [
          "<string>"
        ],
        "result.subscriptions[].usageLimits": [
          {}
        ],
        "result.subscriptions[].cost": "<string>"
      }
    ],
    "result.cardinality": 123
  }
}
The Subscription Enumeration operations allow you to explore the entire configuration space of a pricing model. You can either enumerate all valid subscriptions or filter them based on specific criteria.

Operations

Two operations are available for working with subscription configurations:
  • subscriptions: Enumerate ALL valid configurations
  • filter: Find configurations matching specific criteria

Enumerate All Subscriptions

Overview

The subscriptions operation calculates and returns every valid subscription configuration possible in your pricing model. This is useful for:
  • Understanding the full scope of your pricing model
  • Analyzing configuration space size
  • Generating test cases
  • Identifying unexpected configurations
Configuration spaces can grow exponentially. A model with 5 plans and 10 add-ons might have hundreds or thousands of valid configurations. Use filtering for large models.

Request

POST /api/v1/pricing/analysis
pricingFile
file
required
The YAML file containing the pricing configuration
operation
string
required
Must be subscriptions
solver
string
required
Solver to use:
  • minizinc - MiniZinc solver
  • choco - Choco solver

Example Request

curl -X POST "http://localhost:3000/api/v1/pricing/analysis" \
  -F "[email protected]" \
  -F "operation=subscriptions" \
  -F "solver=minizinc"

Response

The endpoint returns a job ID immediately. Poll for results:

Initial Response (202 Accepted)

{
  "jobId": "job-subs-a1b2c3",
  "status": "PENDING",
  "submittedAt": "2026-03-05T16:30:00Z"
}

Completed Response (200 OK)

jobId
string
required
Job identifier
status
string
required
Job status: COMPLETED
submittedAt
string
required
ISO 8601 timestamp of submission
startedAt
string
required
ISO 8601 timestamp of start
completedAt
string
required
ISO 8601 timestamp of completion
result
object
required
Enumeration results
result.subscriptions
array
required
Array of all valid subscription configurations
result.subscriptions[].plan
string
required
Name of the plan
result.subscriptions[].addOns
string[]
required
List of add-on names (empty array if no add-ons)
result.subscriptions[].features
string[]
List of features included (MiniZinc solver)
result.subscriptions[].usageLimits
object[]
Usage limits with their values (MiniZinc solver)
result.subscriptions[].cost
string
required
Total cost with currency (e.g., “21 USD”)
result.cardinality
integer
required
Total number of valid configurations

Example Response

{
  "jobId": "job-subs-a1b2c3",
  "status": "COMPLETED",
  "submittedAt": "2026-03-05T16:30:00Z",
  "startedAt": "2026-03-05T16:30:02Z",
  "completedAt": "2026-03-05T16:30:45Z",
  "result": {
    "subscriptions": [
      {
        "plan": "FREE",
        "addOns": [],
        "features": [
          "latexEditor",
          "realTimeCollaboration",
          "projects",
          "templates",
          "fastCompileServers"
        ],
        "usageLimits": [
          {"name": "maxCollaboratorsPerProject", "value": 1},
          {"name": "compileTimeoutLimit", "value": 1}
        ],
        "cost": "0 USD"
      },
      {
        "plan": "STANDARD",
        "addOns": [],
        "features": [
          "latexEditor",
          "realTimeCollaboration",
          "projects",
          "templates",
          "fastestCompileServers",
          "realTimeTrackChanges",
          "fullDocumentHistory",
          "advancedReferenceSearch",
          "gitIntegration",
          "symbolPalette",
          "githubIntegration",
          "dropboxIntegration",
          "mendeleyIntegration",
          "zoteroIntegration",
          "prioritySupport"
        ],
        "usageLimits": [
          {"name": "maxCollaboratorsPerProject", "value": 11},
          {"name": "compileTimeoutLimit", "value": 4}
        ],
        "cost": "21 USD"
      },
      {
        "plan": "PROFESSIONAL",
        "addOns": [],
        "features": [
          "latexEditor",
          "realTimeCollaboration",
          "projects",
          "templates",
          "fastestCompileServers",
          "realTimeTrackChanges",
          "fullDocumentHistory",
          "advancedReferenceSearch",
          "gitIntegration",
          "symbolPalette",
          "githubIntegration",
          "dropboxIntegration",
          "mendeleyIntegration",
          "zoteroIntegration",
          "prioritySupport"
        ],
        "usageLimits": [
          {"name": "maxCollaboratorsPerProject", "value": null},
          {"name": "compileTimeoutLimit", "value": 4}
        ],
        "cost": "42 USD"
      }
    ],
    "cardinality": 3
  }
}

Filter Subscriptions

Overview

The filter operation returns only configurations that match specific criteria. This is more efficient than enumerating all subscriptions and filtering client-side.

Request

pricingFile
file
required
The YAML file containing the pricing configuration
operation
string
required
Must be filter
solver
string
required
Must be minizinc (Choco not yet supported for filtering)
filters
string
required
JSON string with filter criteria:
{
  "minPrice": 10,
  "maxPrice": 50,
  "maxSubscriptionSize": 3,
  "features": ["gitIntegration", "prioritySupport"],
  "usageLimits": [
    {"name": "maxCollaboratorsPerProject", "value": 5}
  ]
}

Example Request

curl -X POST "http://localhost:3000/api/v1/pricing/analysis" \
  -F "[email protected]" \
  -F "operation=filter" \
  -F "solver=minizinc" \
  -F 'filters={"minPrice": 10, "maxPrice": 30, "features": ["gitIntegration"]}'

Response

Identical structure to subscriptions operation, but only includes configurations matching the filter criteria:
{
  "jobId": "job-filter-x7y8z9",
  "status": "COMPLETED",
  "submittedAt": "2026-03-05T17:00:00Z",
  "startedAt": "2026-03-05T17:00:02Z",
  "completedAt": "2026-03-05T17:00:15Z",
  "result": {
    "subscriptions": [
      {
        "plan": "STANDARD",
        "addOns": [],
        "features": [
          "latexEditor",
          "realTimeCollaboration",
          "gitIntegration",
          "prioritySupport"
        ],
        "cost": "21 USD"
      }
    ],
    "cardinality": 1
  }
}

Use Cases

Generate a complete catalog of all available subscription options for documentation or sales enablement.
# Get all subscriptions
job = create_analysis_job(
    pricing_file='pricing.yaml',
    operation='subscriptions',
    solver='minizinc'
)

result = wait_for_job(job['jobId'])

# Generate catalog
for sub in result['subscriptions']:
    print(f"\n{sub['plan']} - {sub['cost']}")
    print(f"  Features: {len(sub['features'])}")
    if sub['addOns']:
        print(f"  Add-ons: {', '.join(sub['addOns'])}")
Analyze the size and distribution of your configuration space.
const result = await getSubscriptions('pricing.yaml');

console.log(`Total configurations: ${result.cardinality}`);

const byPrice = result.subscriptions.reduce((acc, sub) => {
  const price = parseFloat(sub.cost);
  acc[price] = (acc[price] || 0) + 1;
  return acc;
}, {});

console.log('Configurations by price:', byPrice);
Build an interactive UI that shows available plans based on user selections.
async function getAvailablePlans(selectedFeatures, maxPrice) {
  const formData = new FormData();
  formData.append('pricingFile', pricingFile);
  formData.append('operation', 'filter');
  formData.append('solver', 'minizinc');
  formData.append('filters', JSON.stringify({
    features: selectedFeatures,
    maxPrice: maxPrice
  }));
  
  const job = await createJob(formData);
  const result = await waitForJob(job.jobId);
  
  return result.subscriptions;
}

// User selects features
const plans = await getAvailablePlans(
  ['gitIntegration', 'prioritySupport'],
  50
);

displayPlans(plans);
Generate comprehensive test cases covering all valid configurations.
import pytest

def generate_subscription_tests():
    result = get_all_subscriptions('pricing.yaml')
    
    test_cases = []
    for sub in result['subscriptions']:
        test_cases.append({
            'plan': sub['plan'],
            'addOns': sub['addOns'],
            'expected_cost': float(sub['cost'].split()[0])
        })
    
    return test_cases

@pytest.mark.parametrize('config', generate_subscription_tests())
def test_subscription_pricing(config):
    actual = calculate_price(config['plan'], config['addOns'])
    assert actual == config['expected_cost']

Performance Considerations

Configuration Space Growth

The number of valid configurations grows based on:
  • Number of plans: Linear growth (P configurations minimum)
  • Number of add-ons per plan: Exponential growth (2^A possible combinations per plan)
  • Add-on constraints: Reduces actual configurations
Example:
  • 3 plans with no add-ons = 3 configurations
  • 3 plans with 5 available add-ons each = up to 3 × 2^5 = 96 configurations
  • 10 plans with 20 add-ons = potentially 10,000+ configurations

Execution Time

Configuration SpaceExecution Time
< 100 configs5-15s
100-500 configs15-60s
500-1000 configs1-3 min
> 1000 configs3+ min
For large configuration spaces, use the filter operation with constraints to reduce processing time.

Memory Usage

Large result sets consume memory both server-side and client-side:
  • Each configuration: ~1-5 KB (depending on features/limits)
  • 1000 configurations: ~1-5 MB JSON response
  • 10,000 configurations: ~10-50 MB JSON response
For models with 5,000+ configurations, consider implementing pagination or using filters to retrieve results in batches.

Solver Differences

MiniZinc Solver

Advantages:
  • Returns complete feature lists
  • Returns usage limit values
  • Supports filtering operation
  • More detailed output
Output:
{
  "plan": "STANDARD",
  "addOns": [],
  "features": ["feature1", "feature2", ...],
  "usageLimits": [{"name": "limit1", "value": 10}],
  "cost": "21 USD"
}

Choco Solver

Advantages:
  • Faster for validation and enumeration
  • Better for very large models
  • Integrated validation in response
Limitations:
  • No filtering support yet
  • Features/limits must be calculated client-side
Output:
{
  "plan": "STANDARD",
  "addOns": [],
  "cost": "21 USD"
}

Implementation

The subscription operations are implemented in:
  • Subscriptions: /home/daytona/workspace/source/analysis_api/src/api/analysis.ts:264
  • Filter: /home/daytona/workspace/source/analysis_api/src/api/analysis.ts:301
  • MiniZinc service: /home/daytona/workspace/source/analysis_api/src/services/minizinc.service.ts

Optimal Configuration

Find just the best configuration instead of all configurations

Validation

Validate pricing model before enumeration

Build docs developers (and LLMs) love