Deterministic pricing your AI agents can trust
Quotix MCP is a pricing brain for chat and automation workflows. Your agent asks for a quote — Quotix returns a verified total from fixed rules. No model guesses the number.
Overview
Quotix MCP exposes a company's pricing engine as MCP tools. An AI agent (in CloseBot, n8n, Claude, a custom app…) collects the job details from the customer, then calls Quotix to turn those details into a priced quote.
Deterministic
Prices come from rules stored per business — line-item formulas, surcharge modifiers, and a tax rate. The same inputs always produce the same total. No LLM sits in the pricing path.
Multi-tenant
One server, many businesses. A request identifies the business by its platform account ID (e.g. a GoHighLevel sub-account), and Quotix loads that company's rules.
Schema-driven
Each service type publishes a JSON Schema of the fields it needs. Your agent can discover the questions to ask instead of hard-coding them.
Separate from the Quotix web builder
This is the chat/automation pricing service. It is intentionally independent of the Quotix website form builder — see the FAQ.
How it works
1 — Identify the business
Every call carries platform + external_id. Quotix maps that pair to a tenant, then loads the pricing engine for the requested service_type.
(platform, external_id) → tenant → pricing engine (by service_type) "gohighlevel" + "ghl_sub_ny_445" → Empire HVAC NY → "AC Replacement" engine
2 — Discover the required inputs
Call get_quote_schema to get the engine's input_schema (JSON Schema) and its required_fields. Your agent asks the customer for exactly those.
3 — Get the verified quote
Call calculate_quote with the complete inputs. Quotix validates against the schema, runs the deterministic calculation, and returns the total plus a full breakdown.
Connection details
| Setting | Value |
|---|---|
| MCP endpoint | https://mcp.quotix.ai/mcp |
| Transport | Streamable HTTP |
| Auth header | Authorization: Bearer <QUOTIX_API_KEY> |
| API key | Issued by Quotix during onboarding. It is not a Supabase or GoHighLevel key — it is a dedicated secret for this API. Treat it like a password. |
| Without a valid key | 401 Unauthorized |
| Health check (no auth) | GET https://mcp.quotix.ai/health |
mcp.quotix.ai is being provisioned. During onboarding you may be given a staging URL — substitute it wherever this page shows https://mcp.quotix.ai.Quickstart
Pick your client. All of them need the endpoint URL and the Authorization header above.
n8n (MCP Client node)
- Add an MCP Client (or Tool MCP) node.
- Server URL:
https://mcp.quotix.ai/mcp - Transport: HTTP Streamable (not legacy SSE)
- Add header
Authorization: Bearer <QUOTIX_API_KEY>(store it as a credential) - Select the tool:
get_quote_schemaorcalculate_quote
/mcp, raise read/send timeouts to 300s+, and pass the Authorization header through. Ask the Quotix team for the reference n8n workflow.Claude Desktop / generic MCP client
Clients that speak HTTP transport can point straight at the endpoint:
{
"mcpServers": {
"quotix-pricing": {
"url": "https://mcp.quotix.ai/mcp",
"headers": {
"Authorization": "Bearer <QUOTIX_API_KEY>"
}
}
}
}mcp-remote:
{
"mcpServers": {
"quotix-pricing": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.quotix.ai/mcp",
"--header", "Authorization: Bearer <QUOTIX_API_KEY>"]
}
}
}Cursor / VS Code
Add the same block to .cursor/mcp.json (project) or your global MCP config.
Python (fastmcp Client)
from fastmcp import Client
async with Client("https://mcp.quotix.ai/mcp", auth="<QUOTIX_API_KEY>") as client:
schema = await client.call_tool("get_quote_schema", {
"platform": "gohighlevel",
"external_id": "ghl_sub_ny_445",
"service_type": "AC Replacement",
})
print(schema.structured_content["required_fields"])curl (connectivity check)
The MCP endpoint speaks JSON-RPC over HTTP. A quick tools/list:
curl -sS https://mcp.quotix.ai/mcp \
-H "Authorization: Bearer $QUOTIX_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Tools reference
get_quote_schema
Return the input schema and required fields for a tenant's service type. Call this first so your agent asks the customer the right questions.
| Argument | Type | Example |
|---|---|---|
platform | string | gohighlevel |
external_id | string | ghl_sub_ny_445 |
service_type | string | AC Replacement |
Success (structured content)
{
"engine_id": "b7c1e0a2-...",
"engine_name": "Residential AC Replacement",
"service_type": "AC Replacement",
"tenant_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"input_schema": {
"type": "object",
"properties": { "tonnage_capacity": { "type": "number" }, "...": {} },
"required": ["tonnage_capacity", "us_state_location", "site_accessibility"],
"additionalProperties": false
},
"required_fields": ["tonnage_capacity", "us_state_location", "site_accessibility"]
}calculate_quote
Calculate a verified quote once every required field is collected.
| Argument | Type | Notes |
|---|---|---|
platform | string | gohighlevel |
external_id | string | the business's platform account ID |
service_type | string | must match a configured engine |
inputs | object | keys & types defined by input_schema |
Success (structured content)
{
"engine_used": "Residential AC Replacement",
"engine_id": "b7c1e0a2-...",
"service_type": "AC Replacement",
"tenant_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"total": 6416.25,
"breakdown": {
"line_items": [
{ "key": "equipment", "label": "Base Equipment", "amount": 4900.0 },
{ "key": "labor", "label": "Installation Labor", "amount": 1000.0 }
],
"modifiers": [],
"subtotal": 5900.0,
"tax": 516.25,
"tax_rate": 0.0875,
"total": 6416.25
}
}Schema-gate workflow
Use this loop so the agent never invents missing job details.
- Call
get_quote_schemawithplatform,external_id,service_type. - Collect every value in
required_fieldsfrom the customer. - Call
calculate_quotewith the completeinputs. - If it returns
VALIDATION_ERROR, readdetails.missing_fields, ask those questions, and retry.
Errors
MCP tool errors return isError: true with a structured payload. The REST API returns the same body with an HTTP status.
| Code | When | REST status |
|---|---|---|
| TENANT_NOT_FOUND | Unknown platform + external_id — the business is not onboarded | 404 |
| ENGINE_NOT_FOUND | The service_type is not configured for that tenant | 404 |
| VALIDATION_ERROR | Missing or invalid inputs — details.missing_fields lists what to ask for | 422 |
| CONFIG_INVALID | The tenant's engine JSON is malformed (Quotix is alerted) | 500 |
Error payload
{
"error": "VALIDATION_ERROR",
"message": "Required quote inputs are missing or invalid.",
"details": { "missing_fields": ["site_accessibility"] }
}Also expect standard 401 Unauthorized when the Bearer token is missing or wrong.
Worked example
The Empire HVAC NY pilot tenant. Copy these values to test end to end.
| Field | Value |
|---|---|
platform | gohighlevel |
external_id | ghl_sub_ny_445 |
service_type | AC Replacement |
tenant_id | a1b2c3d4-e5f6-7890-abcd-ef1234567890 |
Request — calculate_quote
{
"platform": "gohighlevel",
"external_id": "ghl_sub_ny_445",
"service_type": "AC Replacement",
"inputs": {
"tonnage_capacity": 3,
"us_state_location": "New York",
"site_accessibility": "Attic - pull-down access"
}
}Result
| Line | Formula | Amount |
|---|---|---|
| Base Equipment | 2500 + (3 × 800) | $4,900.00 |
| Installation Labor | 8 × 125 | $1,000.00 |
| Subtotal | $5,900.00 | |
| Tax | 5900 × 0.0875 | $516.25 |
| Total | $6,416.25 |
site_accessibility to "Rooftop - ladder access" and a +$350 modifier is applied — a new subtotal, instantly, from the same rules.Pricing engines
Each service_type is a JSON engine: an input_schema (what to collect) and a calculation (how to price). Engines are data — new service types are a config change, not a code deploy.
{
"schema_version": "1.0",
"display_name": "Residential AC Replacement",
"shareable": true,
"input_schema": {
"type": "object",
"properties": {
"tonnage_capacity": { "type": "number", "minimum": 1, "maximum": 10 },
"us_state_location": { "type": "string", "enum": ["New York", "New Jersey", "Connecticut"] },
"site_accessibility": { "type": "string", "enum": ["Attic - pull-down access", "Rooftop - ladder access", "..."] }
},
"required": ["tonnage_capacity", "us_state_location", "site_accessibility"],
"additionalProperties": false
},
"calculation": {
"line_items": [
{ "key": "equipment", "label": "Base Equipment",
"formula": "base_equipment + (tonnage_capacity * ton_multiplier)",
"vars": { "base_equipment": 2500, "ton_multiplier": 800 } },
{ "key": "labor", "label": "Installation Labor",
"formula": "labor_hours * labor_rate",
"vars": { "labor_hours": 8, "labor_rate": 125 } }
],
"modifiers": [
{ "when": { "field": "site_accessibility", "equals": "Rooftop - ladder access" },
"add": 350, "label": "Rooftop access surcharge" }
],
"tax": { "rate": 0.0875 }
}
}| Part | Meaning |
|---|---|
input_schema | Standard JSON Schema. Also returned by get_quote_schema and shareable with GHL / CloseBot so questions aren't hard-coded. |
line_items[].formula | Arithmetic only — + - * /, numeric literals, and names from vars + the customer inputs. No functions, no arbitrary code. |
modifiers[] | Conditional surcharges: when a field equals a value, add an amount. |
tax.rate | Applied to the subtotal (line items + modifiers). |
REST API
The same pricing brain over plain HTTP, for clients that don't speak MCP. Same Bearer key.
| Method & path | Auth | Purpose |
|---|---|---|
GET /health | — | Liveness + database connectivity |
GET /v1/status | Bearer | Confirm your API key works |
POST /v1/quotes/calculate | Bearer | Verified quote — body is the calculate_quote payload |
GET /v1/engines/schema | Bearer | Input schema — query platform, external_id, service_type |
GET /v1/tenants/{tenant_id}/engines | Bearer | List a tenant's configured services |
curl -sS -X POST https://mcp.quotix.ai/v1/quotes/calculate \
-H "Authorization: Bearer $QUOTIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"platform": "gohighlevel",
"external_id": "ghl_sub_ny_445",
"service_type": "AC Replacement",
"inputs": { "tonnage_capacity": 3, "us_state_location": "New York",
"site_accessibility": "Attic - pull-down access" }
}'Interactive API reference (OpenAPI / Swagger UI): https://mcp.quotix.ai/docs
Request access
Onboarding is done by the Quotix team (no self-serve yet). To get an API key and your tenant configured, send us:
| What | Why |
|---|---|
| Company name & primary contact | Creates your tenant record |
| GoHighLevel sub-account ID (and CloseBot bot ID if used) | The external_id that routes requests to you |
| Service types to configure | e.g. AC Replacement, Rooftop Installation, Ductwork |
| Signed-off pricing formulas per service | Line items, modifiers, tax rate — the deterministic rules |
| Required customer inputs per service | The fields your chat must collect |
| Regions / enums | e.g. NY, NJ, CT and any option lists |
| Staging or production key | We issue keys per environment |
FAQ
Why can the total differ from the Quotix website form?
Expected in V1. The website form builder and this chat-pricing service are separate systems until they're merged. This service is the source of truth for agent-driven quotes.
Can we add a new service type without a developer?
Yes. A service type is a JSON engine (schema + calculation). Add the file and re-seed — no application redeploy.
Is the API key safe to share?
Only with trusted systems (your n8n instance, your team). Treat it like a password. Production and staging use different keys.
Does Quotix generate the PDF / write back to GoHighLevel?
No. Quotix returns price JSON only. PDF proposals and GHL write-back are handled in your n8n workflow (GoHighLevel has its own MCP server for write-back).
Is there an LLM in the pricing path?
No. The agent collects inputs; Quotix validates them against the schema and runs fixed arithmetic. Same inputs → same price, every time.