{
  "components": {
    "schemas": {
      "Billing": {
        "properties": {
          "cached": {
            "description": "True when the answer came from the cache: nothing was charged.",
            "type": "boolean"
          },
          "daily_limit": {
            "type": "integer"
          },
          "estimates_charged": {
            "type": "integer"
          },
          "free_calculations_remaining": {
            "type": "integer"
          },
          "mode": {
            "enum": [
              "account",
              "anonymous"
            ],
            "type": "string"
          },
          "paid_token_balance": {
            "type": "integer"
          },
          "paid_tokens_charged": {
            "type": "integer"
          },
          "remaining_today": {
            "type": "integer"
          },
          "resets_at": {
            "description": "Next 00:00 UTC, when the daily counters reset.",
            "type": "string"
          },
          "total_available_runs": {
            "type": "integer"
          },
          "used_free_allowance": {
            "type": "boolean"
          }
        },
        "required": [
          "mode"
        ],
        "type": "object"
      },
      "Error": {
        "example": {
          "code": "unknown_product",
          "error": "Unknown product: 'nope'",
          "request_id": "req_0123456789abcdef"
        },
        "properties": {
          "code": {
            "type": "string"
          },
          "error": {
            "type": "string"
          },
          "request_id": {
            "type": "string"
          }
        },
        "required": [
          "error",
          "code"
        ],
        "type": "object"
      },
      "EstimateRequest": {
        "additionalProperties": false,
        "example": {
          "currency": "EUR",
          "product": "mongodb-replica-set",
          "provider": "hetzner",
          "workload": {
            "node_count": 3
          }
        },
        "properties": {
          "currency": {
            "description": "Optional display currency (USD or EUR).",
            "type": "string"
          },
          "product": {
            "description": "Product id from GET /agent/v1/products.",
            "type": "string"
          },
          "provider": {
            "description": "Optional provider id from GET /agent/v1/reference. Defaults to the first configured provider.",
            "type": "string"
          },
          "workload": {
            "additionalProperties": {
              "type": [
                "string",
                "number",
                "boolean",
                "null"
              ]
            },
            "description": "Parameter values keyed by parameter name (see GET /agent/v1/products/{id}). Values may be strings, numbers or booleans. Unknown names are rejected with 422.",
            "type": "object"
          }
        },
        "required": [
          "product",
          "workload"
        ],
        "type": "object"
      },
      "EstimateResponse": {
        "example": {
          "assumptions": [
            "Prices are indicative monthly list prices for dedicated servers or VMs from the provider catalog snapshot; they are not a quote."
          ],
          "billing": {
            "cached": false,
            "daily_limit": 20,
            "estimates_charged": 1,
            "mode": "account",
            "remaining_today": 19,
            "resets_at": "2026-10-20T00:00:00Z"
          },
          "currency": "EUR",
          "fx_snapshot_date": "2026-03-13",
          "line_items": [
            {
              "display_subtotal": {
                "currency": "EUR",
                "hourly": 0.1794,
                "monthly": 111.9,
                "setup": 0
              },
              "purpose": "primary",
              "quantity": 3,
              "server": {
                "model": "AX41-NVME"
              }
            }
          ],
          "price_snapshot_date": "2026-03-14",
          "price_sources": [
            {
              "collected_at": "2026-03-14T00:00:00Z",
              "offer": "AX41-NVME",
              "provider": "hetzner",
              "source_url": "https://www.hetzner.com/dedicated-rootserver/ax41-nvme"
            }
          ],
          "product": {
            "id": "mongodb-replica-set",
            "name": "MongoDB Replica Set"
          },
          "provider": {
            "id": "hetzner",
            "name": "Hetzner"
          },
          "request_id": "req_0123456789abcdef",
          "result_url": "https://calc.onpremeasy.com/?calc=calc_abc",
          "summary": "Balanced cluster",
          "totals": {
            "currency": "EUR",
            "hourly": 0.1794,
            "monthly": 111.9,
            "setup": 0
          }
        },
        "properties": {
          "assumptions": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "base_totals": {
            "description": "Totals in EUR and USD before display-currency conversion.",
            "type": "object"
          },
          "billing": {
            "$ref": "#/components/schemas/Billing"
          },
          "component_totals": {
            "items": {
              "type": "object"
            },
            "type": "array"
          },
          "currency": {
            "type": "string"
          },
          "fx_snapshot_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "line_items": {
            "items": {
              "$ref": "#/components/schemas/LineItem"
            },
            "type": "array"
          },
          "parameters": {
            "items": {
              "type": "object"
            },
            "type": "array"
          },
          "price_snapshot_date": {
            "description": "Newest collection date among the chosen offers (YYYY-MM-DD).",
            "type": [
              "string",
              "null"
            ]
          },
          "price_sources": {
            "items": {
              "$ref": "#/components/schemas/PriceSource"
            },
            "type": "array"
          },
          "pricing": {
            "$ref": "#/components/schemas/PricingMetadata"
          },
          "product": {
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name"
            ],
            "type": "object"
          },
          "provider": {
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name"
            ],
            "type": "object"
          },
          "request_id": {
            "type": "string"
          },
          "result_url": {
            "description": "Human-readable result page. For anonymous estimates it carries a server-generated owner_key; for API-key estimates it opens after the key owner signs in. Null when result storage is unavailable.",
            "type": [
              "string",
              "null"
            ]
          },
          "summary": {
            "type": "string"
          },
          "totals": {
            "$ref": "#/components/schemas/Money"
          }
        },
        "required": [
          "request_id",
          "product",
          "provider",
          "currency",
          "totals",
          "line_items",
          "assumptions",
          "price_sources",
          "price_snapshot_date",
          "billing",
          "result_url"
        ],
        "type": "object"
      },
      "LineItem": {
        "properties": {
          "component_kind": {
            "type": "string"
          },
          "component_label": {
            "type": "string"
          },
          "display_subtotal": {
            "$ref": "#/components/schemas/Money"
          },
          "purpose": {
            "type": "string"
          },
          "quantity": {
            "type": "integer"
          },
          "server": {
            "type": "object"
          },
          "subtotal": {
            "type": "object"
          }
        },
        "required": [
          "quantity",
          "purpose",
          "server",
          "display_subtotal"
        ],
        "type": "object"
      },
      "Money": {
        "properties": {
          "currency": {
            "type": "string"
          },
          "hourly": {
            "type": "number"
          },
          "monthly": {
            "type": "number"
          },
          "setup": {
            "type": "number"
          }
        },
        "required": [
          "currency",
          "monthly",
          "hourly",
          "setup"
        ],
        "type": "object"
      },
      "Pack": {
        "properties": {
          "checkout_url": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "tokens": {
            "type": "integer"
          }
        },
        "required": [
          "code",
          "name",
          "tokens",
          "description",
          "checkout_url"
        ],
        "type": "object"
      },
      "Parameter": {
        "properties": {
          "default": {},
          "description": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "minimum": {
            "description": "Recommended lower bound for sizing. Advisory: the API only rejects negative numbers.",
            "type": "number"
          },
          "name": {
            "type": "string"
          },
          "placeholder": {
            "type": "string"
          },
          "required": {
            "type": "boolean"
          },
          "type": {
            "type": "string"
          }
        },
        "required": [
          "name",
          "label",
          "type",
          "required"
        ],
        "type": "object"
      },
      "PaymentRequired": {
        "description": "Quota exhausted. checkout_url and packs are present only while the operator has payments enabled; quota_resets_at, retry_after_seconds and contact_url are always there.",
        "example": {
          "billing": {
            "daily_limit": 20,
            "mode": "account",
            "remaining_today": 0,
            "resets_at": "2026-10-20T00:00:00Z"
          },
          "code": "payment_required",
          "contact_url": "mailto:support@onpremeasy.com",
          "error": "The free daily estimate quota of this account is used up.",
          "message_for_human": "This API key's account has used its free estimates for today (UTC day). The quota resets at 2026-10-20T00:00:00Z. To ask for more capacity, contact mailto:support@onpremeasy.com.",
          "quota_resets_at": "2026-10-20T00:00:00Z",
          "request_id": "req_0123456789abcdef",
          "retry_after_seconds": 21600
        },
        "examples": [
          {
            "billing": {
              "daily_limit": 20,
              "free_calculations_remaining": 0,
              "mode": "account",
              "paid_token_balance": 0,
              "remaining_today": 0,
              "total_available_runs": 0
            },
            "checkout_url": "https://calc.onpremeasy.com/pricing?utm_source=agent\u0026utm_medium=402\u0026pack=pack_starter",
            "code": "payment_required",
            "error": "The free daily quota and the paid tokens are exhausted. Buy tokens to continue.",
            "message_for_human": "Payments enabled variant: open the checkout link, sign in as the key owner and buy a token pack.",
            "packs": [
              {
                "checkout_url": "https://calc.onpremeasy.com/pricing?utm_source=agent\u0026utm_medium=402\u0026pack=pack_starter",
                "code": "pack_starter",
                "description": "Good for occasional infrastructure quotes.",
                "name": "Starter",
                "tokens": 25
              }
            ],
            "quota_resets_at": "2026-10-20T00:00:00Z",
            "request_id": "req_0123456789abcdef",
            "retry_after_seconds": 21600
          }
        ],
        "properties": {
          "billing": {
            "$ref": "#/components/schemas/Billing"
          },
          "checkout_url": {
            "description": "Only when payments are enabled.",
            "type": "string"
          },
          "code": {
            "enum": [
              "payment_required"
            ],
            "type": "string"
          },
          "contact_url": {
            "description": "Where a human can ask for more capacity. Present when payments are off.",
            "type": "string"
          },
          "error": {
            "type": "string"
          },
          "message_for_human": {
            "type": "string"
          },
          "packs": {
            "description": "Only when payments are enabled.",
            "items": {
              "$ref": "#/components/schemas/Pack"
            },
            "type": "array"
          },
          "quota_resets_at": {
            "description": "Next 00:00 UTC (RFC 3339).",
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "request_id": {
            "type": "string"
          },
          "retry_after_seconds": {
            "type": "integer"
          }
        },
        "required": [
          "error",
          "code",
          "billing",
          "message_for_human"
        ],
        "type": "object"
      },
      "PriceReference": {
        "properties": {
          "answer": {
            "type": "string"
          },
          "configuration_summary": {
            "type": "string"
          },
          "currency": {
            "description": "Always USD.",
            "type": "string"
          },
          "fx_snapshot_date": {
            "type": "string"
          },
          "methodology": {
            "description": "How the range was built and what it excludes. Automatically generated from public price lists, not human reviewed.",
            "type": "string"
          },
          "monthly_high": {
            "description": "Whole dollars per month: median server of the combined all-provider shortlist times the baseline server count.",
            "type": "integer"
          },
          "monthly_low": {
            "description": "Whole dollars per month: the cheapest of the per-provider from-prices (cheapest server in a provider's own shortlist, at least 2 vCPU and 4 GB RAM) times the baseline server count.",
            "type": "integer"
          },
          "node_count": {
            "type": "integer"
          },
          "price_snapshot_date": {
            "type": "string"
          },
          "price_sources": {
            "items": {
              "properties": {
                "provider": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                }
              },
              "required": [
                "provider",
                "url"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "provider_prices": {
            "items": {
              "properties": {
                "memory_gb": {
                  "type": "number"
                },
                "monthly": {
                  "description": "Whole dollars per month: this provider's cheapest shortlisted server times the baseline server count. Ordered cheapest first.",
                  "type": "integer"
                },
                "monthly_per_server": {
                  "description": "Whole dollars per server per month.",
                  "type": "integer"
                },
                "provider": {
                  "type": "string"
                },
                "provider_id": {
                  "type": "string"
                },
                "region": {
                  "type": "string"
                },
                "server": {
                  "type": "string"
                },
                "vcpu": {
                  "type": "integer"
                }
              },
              "required": [
                "provider",
                "provider_id",
                "server",
                "monthly"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "provider_text": {
            "type": "string"
          },
          "range_text": {
            "type": "string"
          },
          "reference_workload": {
            "items": {
              "properties": {
                "label": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              },
              "required": [
                "name",
                "value"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "shortlist_size": {
            "type": "integer"
          },
          "topology": {
            "type": "string"
          }
        },
        "required": [
          "monthly_low",
          "monthly_high",
          "currency",
          "price_snapshot_date",
          "methodology",
          "answer"
        ],
        "type": "object"
      },
      "PriceSource": {
        "properties": {
          "collected_at": {
            "type": "string"
          },
          "offer": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "provider_name": {
            "type": "string"
          },
          "raw_snapshot_ref": {
            "type": "string"
          },
          "source_url": {
            "type": "string"
          }
        },
        "required": [
          "provider",
          "offer"
        ],
        "type": "object"
      },
      "PricingMetadata": {
        "description": "Rate provenance of this response, additive. One EUR/USD rate was resolved for the whole response: the same one prices ranking, line items, totals and these fields. Same object as the pricing block of POST /api/calculate and GET /api/config.",
        "properties": {
          "comparison_currency": {
            "type": "string"
          },
          "display_currency": {
            "type": "string"
          },
          "exchange_rate": {
            "description": "Comparison (USD) to display-currency rate used for totals.",
            "type": "number"
          },
          "exchange_rate_as_of": {
            "type": "string"
          },
          "exchange_rate_origin": {
            "description": "ecb_live, ecb_cached or snapshot.",
            "type": "string"
          },
          "exchange_rate_source": {
            "type": "string"
          },
          "exchange_rate_source_id": {
            "type": "string"
          },
          "exchange_rate_stale": {
            "description": "True when the rate is a snapshot fallback after a live failure or a last-good ECB rate older than 48h.",
            "type": "boolean"
          },
          "exchange_rate_url": {
            "type": "string"
          },
          "ranking": {
            "description": "Which EUR/USD rate produced the comparison (USD) prices behind ranking and totals: mode live_rate or published_baked, exchange_rate, exchange_rate_as_of and offer counts.",
            "type": "object"
          }
        },
        "type": "object"
      },
      "Problem": {
        "properties": {
          "message": {
            "type": "string"
          },
          "parameter": {
            "type": "string"
          }
        },
        "required": [
          "parameter",
          "message"
        ],
        "type": "object"
      },
      "ProductDetail": {
        "properties": {
          "category": {
            "type": "string"
          },
          "composition_mode": {
            "type": "string"
          },
          "dependency_targets": {
            "items": {
              "properties": {
                "category": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "notes": {
                  "type": "string"
                },
                "product_id": {
                  "type": "string"
                },
                "relationship": {
                  "type": "string"
                },
                "required": {
                  "type": "boolean"
                }
              },
              "required": [
                "name",
                "required"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "deployment_mode": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "estimate_url": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "parameters": {
            "items": {
              "$ref": "#/components/schemas/Parameter"
            },
            "type": "array"
          },
          "price_snapshot_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "reference": {
            "description": "Indicative from-price range for the product's own servers on its reference workload (parameter defaults), the same record the public product page prints. Computed deterministically from the dated public price snapshot, no LLM; null when it cannot be built. NOT a quote and not the estimate endpoint's total: call estimate for a sized answer.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/PriceReference"
              },
              {
                "type": "null"
              }
            ]
          },
          "topology_templates": {
            "items": {
              "properties": {
                "min_nodes": {
                  "type": "integer"
                },
                "name": {
                  "type": "string"
                },
                "notes": {
                  "type": "string"
                },
                "roles": {
                  "items": {
                    "type": "string"
                  },
                  "type": "array"
                }
              },
              "required": [
                "name"
              ],
              "type": "object"
            },
            "type": "array"
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "parameters",
          "topology_templates",
          "dependency_targets",
          "price_snapshot_date",
          "estimate_url"
        ],
        "type": "object"
      },
      "ProductList": {
        "example": {
          "count": 1,
          "items": [
            {
              "describe_url": "/agent/v1/products/mongodb-replica-set",
              "id": "mongodb-replica-set",
              "name": "MongoDB Replica Set",
              "required_parameters": [
                "node_count"
              ],
              "summary": "Three-node replica set."
            }
          ],
          "price_snapshot_date": "2026-03-14"
        },
        "properties": {
          "count": {
            "type": "integer"
          },
          "items": {
            "items": {
              "$ref": "#/components/schemas/ProductSummary"
            },
            "type": "array"
          },
          "price_snapshot_date": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "items",
          "count",
          "price_snapshot_date"
        ],
        "type": "object"
      },
      "ProductSummary": {
        "properties": {
          "category": {
            "type": "string"
          },
          "deployment_mode": {
            "type": "string"
          },
          "describe_url": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "required_parameters": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "summary": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "summary",
          "required_parameters",
          "describe_url"
        ],
        "type": "object"
      },
      "RateLimited": {
        "example": {
          "code": "rate_limited",
          "error": "Too many requests; slow down and retry after the indicated delay",
          "request_id": "req_0123456789abcdef",
          "retry_after_seconds": 6
        },
        "properties": {
          "code": {
            "type": "string"
          },
          "error": {
            "type": "string"
          },
          "request_id": {
            "type": "string"
          },
          "retry_after_seconds": {
            "type": "integer"
          }
        },
        "required": [
          "error",
          "code",
          "retry_after_seconds"
        ],
        "type": "object"
      },
      "Reference": {
        "properties": {
          "billing": {
            "properties": {
              "anonymous": {
                "properties": {
                  "daily_limit": {
                    "type": "integer"
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "enabled",
                  "daily_limit"
                ],
                "type": "object"
              },
              "cache_hits_consume_quota": {
                "description": "Always false: cached answers are free.",
                "type": "boolean"
              },
              "estimate_cost_tokens": {
                "type": "integer"
              },
              "free_allowance_per_account": {
                "type": "integer"
              },
              "keyed_daily_limit": {
                "description": "Free estimates per account per UTC day, summed over all keys.",
                "type": "integer"
              },
              "packs": {
                "description": "Empty while payments are off.",
                "items": {
                  "$ref": "#/components/schemas/Pack"
                },
                "type": "array"
              },
              "payments_enabled": {
                "description": "False by default: no checkout, only the free quotas.",
                "type": "boolean"
              }
            },
            "required": [
              "estimate_cost_tokens",
              "free_allowance_per_account",
              "packs",
              "anonymous",
              "payments_enabled",
              "keyed_daily_limit",
              "cache_hits_consume_quota"
            ],
            "type": "object"
          },
          "currencies": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "default_currency": {
            "type": "string"
          },
          "providers": {
            "items": {
              "properties": {
                "active_count": {
                  "type": "integer"
                },
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "offer_count": {
                  "type": "integer"
                }
              },
              "required": [
                "id",
                "name"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "snapshots": {
            "properties": {
              "fx_snapshot_date": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "price_snapshot_date": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "pricing": {
                "$ref": "#/components/schemas/PricingMetadata"
              }
            },
            "required": [
              "price_snapshot_date",
              "fx_snapshot_date"
            ],
            "type": "object"
          }
        },
        "required": [
          "providers",
          "currencies",
          "default_currency",
          "snapshots",
          "billing"
        ],
        "type": "object"
      },
      "ServiceUnavailable": {
        "description": "503. code capacity_reached is the service-wide daily cap (Retry-After header set); the other codes mean a backend is not configured.",
        "example": {
          "code": "capacity_reached",
          "contact_url": "mailto:support@onpremeasy.com",
          "error": "The service's daily estimate capacity is used up. Try again after the reset.",
          "message_for_human": "The calculator has reached its daily limit of API estimates for all users. It resets at 2026-10-20T00:00:00Z. Cached results and product listings still work. Questions: mailto:support@onpremeasy.com.",
          "quota_resets_at": "2026-10-20T00:00:00Z",
          "request_id": "req_0123456789abcdef",
          "retry_after_seconds": 21600
        },
        "properties": {
          "code": {
            "type": "string"
          },
          "contact_url": {
            "type": "string"
          },
          "error": {
            "type": "string"
          },
          "message_for_human": {
            "type": "string"
          },
          "quota_resets_at": {
            "type": "string"
          },
          "request_id": {
            "type": "string"
          },
          "retry_after_seconds": {
            "type": "integer"
          }
        },
        "required": [
          "error",
          "code"
        ],
        "type": "object"
      },
      "Unauthorized": {
        "example": {
          "code": "authentication_required",
          "error": "An API key is required to run estimates.",
          "how_to_authenticate": "Sign in at https://calc.onpremeasy.com and create an agent API key, then send it as 'Authorization: Bearer ope_live_...'.",
          "request_id": "req_0123456789abcdef"
        },
        "properties": {
          "code": {
            "type": "string"
          },
          "error": {
            "type": "string"
          },
          "how_to_authenticate": {
            "type": "string"
          },
          "request_id": {
            "type": "string"
          }
        },
        "required": [
          "error",
          "code"
        ],
        "type": "object"
      },
      "ValidationError": {
        "example": {
          "code": "invalid_request",
          "error": "Missing required field: Node count",
          "problems": [
            {
              "message": "Missing required field: Node count",
              "parameter": "node_count"
            }
          ],
          "request_id": "req_0123456789abcdef"
        },
        "properties": {
          "code": {
            "type": "string"
          },
          "error": {
            "type": "string"
          },
          "problems": {
            "items": {
              "$ref": "#/components/schemas/Problem"
            },
            "type": "array"
          },
          "request_id": {
            "type": "string"
          }
        },
        "required": [
          "error",
          "code",
          "problems"
        ],
        "type": "object"
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "bearerFormat": "ope_live_\u003c64 hex\u003e",
        "description": "Agent API key created by a signed-in user. Optional on estimate: omit it for the anonymous quota.",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "contact": {
      "email": "support@onpremeasy.com",
      "name": "On-Prem Easy support",
      "url": "https://calc.onpremeasy.com"
    },
    "description": "Sizing and indicative monthly price estimates for self-hosted software on dedicated servers and VMs.\n\n**Free, no key:** GET /agent/v1/products, GET /agent/v1/products/{id}, GET /agent/v1/reference.\n\n**Estimates:** POST /agent/v1/estimate. With `Authorization: Bearer ope_live_...` a successful, freshly computed estimate uses one slot of the key owner's free daily quota (default 20 per UTC day, summed over all of the account's keys; the quota is separate from the web calculator's lifetime allowance). Without an Authorization header the anonymous per-IP daily quota applies when the operator enabled it; otherwise the endpoint answers 401. An Authorization header that is present but invalid always answers 401 and never falls back to the anonymous quota.\n\n**Cache hits are free:** when the answer for the same product, workload, provider and currency is already cached, the estimate costs nothing: no quota slot (and, when payments are enabled, no token) is used. The response says so in billing.cached and billing.estimates_charged = 0. A failed estimate is never charged either. The cache is checked before any quota slot, balance or the service-wide cap is touched, so a caller whose daily quota is used up (or an estimate requested while the service-wide cap is reached) still gets a cached answer with 200; only a request that is not cached gets 402 or 503. The per-key and per-address rate limits apply to cache hits too. On a cache hit billing.remaining_today is the unchanged counter, read without reserving (it can be absent if the counter could not be read). This applies to /agent/v1 and the MCP tool only; the web calculator's /api/calculate is unchanged.\n\n**When the quota is used up (402):** the body states the reset time (quota_resets_at, next 00:00 UTC; also retry_after_seconds and the Retry-After header) and contact_url, a human contact for more capacity. While the operator has payments off (the default) the 402 has no checkout_url or packs and mentions no purchase. When the operator turns payments on (GET /agent/v1/reference shows billing.payments_enabled), the 402 also carries checkout_url and packs, and an account whose free daily quota is spent continues on its paid tokens.\n\n**Service-wide cap (503):** a global kill switch bounds fresh (non-cached) estimates per UTC day across anonymous and keyed callers together (default 300). When it is reached, estimate answers 503 code capacity_reached with Retry-After until 00:00 UTC. The keyless GET endpoints keep working.\n\n**Idempotency:** send an optional Idempotency-Key header (1-255 printable ASCII characters) with a key; a repeat of the same request body within 24 hours returns the stored result without a second charge (header Idempotent-Replayed: true). Reusing a key with a different body is 422 idempotency_key_reused. Honoured for API-key callers only.\n\n**Limits:** request body 16 KiB, JSON only; 30 requests/minute per API key and 10 requests/minute per IP for anonymous callers (429 with Retry-After).\n\nPrices are indicative list prices from a dated snapshot, not quotes.",
    "license": {
      "name": "Proprietary",
      "url": "https://calc.onpremeasy.com/terms.html"
    },
    "summary": "Server sizing and indicative monthly pricing for self-hosted software.",
    "title": "On-Prem Easy Agent API",
    "version": "1.0.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/agent/v1/estimate": {
      "post": {
        "description": "Sizes the product for the workload and prices it from the catalog snapshot. A fresh estimate uses 1 slot of the daily quota (API key) or the anonymous quota, counted only on success; a cache hit is free. A fresh estimate calls an LLM and can take several seconds; the server allows up to about a minute (configurable by the operator) before it gives up, so set the client timeout above that.",
        "operationId": "createAgentEstimate",
        "parameters": [
          {
            "description": "Optional. Repeating the same request with the same key within 24 hours returns the stored result without a second charge. API-key callers only.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EstimateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EstimateResponse"
                }
              }
            },
            "description": "Estimate computed. One slot of the free daily quota (or, with payments on and the quota spent, one paid token) was used, unless billing.cached is true: a cache hit uses nothing.",
            "headers": {
              "Idempotent-Replayed": {
                "description": "true when this is a stored replay of an earlier identical request.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "description": "Request id, also in the body.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Body is not valid JSON of the expected shape (code bad_json, invalid_idempotency_key)."
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unauthorized"
                }
              }
            },
            "description": "Missing or invalid API key (code authentication_required or invalid_api_key). Never falls back to the anonymous quota."
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequired"
                }
              }
            },
            "description": "The free daily quota (or, with payments on, also the paid tokens) is exhausted. Retry after quota_resets_at / the Retry-After header, or hand message_for_human (and checkout_url when present) to a person."
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Unknown product (code unknown_product)."
          },
          "405": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Method not allowed."
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Request body larger than 16 KiB (code payload_too_large)."
          },
          "415": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Content-Type is not application/json (code unsupported_media_type)."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            },
            "description": "Invalid request; problems lists each offending parameter (code invalid_request or idempotency_key_reused)."
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimited"
                }
              }
            },
            "description": "Burst rate limit exceeded; see the Retry-After header (code rate_limited)."
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Internal error (code internal)."
          },
          "502": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The recommendation service failed; nothing was charged (code llm_unavailable). Retry later."
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceUnavailable"
                }
              }
            },
            "description": "The service-wide daily estimate cap is reached (code capacity_reached; see Retry-After and quota_resets_at), or a required backend is not configured (code billing_unavailable, quota_unavailable, idempotency_unavailable)."
          }
        },
        "security": [
          {},
          {
            "bearerAuth": []
          }
        ],
        "summary": "Estimate servers and monthly price"
      }
    },
    "/agent/v1/openapi.json": {
      "get": {
        "description": "The OpenAPI 3.1 description of /agent/v1.",
        "operationId": "getAgentOpenAPI",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "OpenAPI document"
          },
          "405": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Method not allowed."
          }
        },
        "security": [
          {}
        ],
        "summary": "This document"
      }
    },
    "/agent/v1/products": {
      "get": {
        "description": "Every product the calculator can size, compact. No key needed.",
        "operationId": "listAgentProducts",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductList"
                }
              }
            },
            "description": "OK"
          },
          "405": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Method not allowed."
          }
        },
        "security": [
          {}
        ],
        "summary": "List products"
      }
    },
    "/agent/v1/products/{id}": {
      "get": {
        "description": "Parameters (name, type, required, minimum, default), topology notes and dependency targets. No key needed.",
        "operationId": "getAgentProduct",
        "parameters": [
          {
            "description": "Product id from the product list.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductDetail"
                }
              }
            },
            "description": "OK"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Unknown product (code unknown_product)."
          },
          "405": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Method not allowed."
          }
        },
        "security": [
          {}
        ],
        "summary": "Describe a product"
      }
    },
    "/agent/v1/reference": {
      "get": {
        "description": "Providers, currencies, quota and payment settings (packs are listed only while payments are on) and snapshot dates. No key needed.",
        "operationId": "getAgentReference",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reference"
                }
              }
            },
            "description": "OK"
          },
          "405": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Method not allowed."
          }
        },
        "security": [
          {}
        ],
        "summary": "Reference data"
      }
    }
  },
  "servers": [
    {
      "description": "Production",
      "url": "https://calc-api.onpremeasy.com"
    },
    {
      "description": "The host serving this document (local or self-hosted runs)",
      "url": "/"
    }
  ]
}
