{
  "openapi": "3.1.0",
  "info": {
    "title": "NeoDefender Teams Phone Calculator API",
    "version": "1.0.1",
    "description": "Public API for calculating Microsoft Teams Phone migration estimates. Estimates are informational only; formal quotes require contact with NeoDefender.",
    "contact": {
      "name": "NeoDefender",
      "email": "hello@neodefender.com",
      "url": "https://neodefender.com/contact"
    },
    "license": {
      "name": "All rights reserved"
    }
  },
  "servers": [
    {
      "url": "https://neodefender.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Teams Phone Calculator API documentation",
    "url": "https://neodefender.com/teams-phone/api"
  },
  "tags": [
    {
      "name": "Teams Phone",
      "description": "Microsoft Teams Phone estimate operations"
    }
  ],
  "paths": {
    "/api/teams-phone-calculator/calculate": {
      "get": {
        "tags": ["Teams Phone"],
        "summary": "Calculate Microsoft Teams Phone estimate using query parameters",
        "description": "Returns an estimated project total, implementation duration and number porting timeline for supported Teams Phone migration scenarios. Rate limit: 10 requests per IP per minute.",
        "operationId": "calculateTeamsPhoneEstimateGet",
        "security": [],
        "parameters": [
          {
            "name": "users",
            "in": "query",
            "required": true,
            "description": "Number of users in scope. Values above 200 return an enterprise contact-required response.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 9999
            },
            "example": 25
          },
          {
            "name": "numbers_to_port",
            "in": "query",
            "required": false,
            "description": "Count of existing phone numbers the customer intends to port to Teams Phone. Used for porting timeline estimate and one-time porting fees.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 1000,
              "default": 0
            },
            "example": 25
          },
          {
            "name": "numbers_ported",
            "in": "query",
            "required": false,
            "deprecated": true,
            "description": "Deprecated alias for numbers_to_port. Accepted for backward compatibility and normalized server-side; responses include a deprecation_warnings entry when used. Will be removed in v1.0.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 1000,
              "default": 0
            }
          },
          {
            "name": "auto_attendants",
            "in": "query",
            "required": false,
            "description": "Number of auto attendants to configure.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 50,
              "default": 0
            },
            "example": 1
          },
          {
            "name": "call_queues",
            "in": "query",
            "required": false,
            "description": "Number of call queues to configure.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 50,
              "default": 0
            },
            "example": 0
          },
          {
            "name": "connectivity",
            "in": "query",
            "required": true,
            "description": "Teams Phone connectivity model.",
            "schema": {
              "type": "string",
              "enum": [
                "microsoft-calling-plans",
                "operator-connect",
                "direct-routing"
              ]
            },
            "example": "microsoft-calling-plans"
          }
        ],
        "responses": {
          "200": {
            "description": "Teams Phone estimate calculated successfully.",
            "headers": {
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer",
                  "example": 10
                },
                "description": "Maximum number of requests allowed per minute."
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer",
                  "example": 9
                },
                "description": "Requests remaining in the current minute window."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeamsPhoneQuoteOutput"
                },
                "examples": {
                  "smallCallingPlans": {
                    "summary": "25 users, 25 numbers, 1 auto attendant",
                    "value": {
                      "estimated_total_usd": 4250,
                      "tier": "small",
                      "duration_weeks_min": 3,
                      "duration_weeks_max": 4,
                      "porting_timeline_weeks_min": 2,
                      "porting_timeline_weeks_max": 3,
                      "porting_timeline": "Porting timeline: typically 2 to 3 weeks after LOA submission, depending on losing carrier response time.",
                      "hypercare_days": 30,
                      "breakdown": {
                        "base": 1500,
                        "number_porting": 2500,
                        "auto_attendants": 250,
                        "call_queues": 0,
                        "connectivity_surcharge": 0
                      },
                      "connectivity_model": "microsoft-calling-plans",
                      "users_count": 25,
                      "is_enterprise": false,
                      "contact_required": true,
                      "contact_message": "This is an estimate. Microsoft licenses and hardware are sold separately. Recurring management is included in NeoDefender Managed Support. Contact us at hello@neodefender.com for a formal quote."
                    }
                  },
                  "directRoutingEnterprise": {
                    "summary": "Direct Routing requires custom planning",
                    "value": {
                      "estimated_total_usd": 0,
                      "tier": "enterprise",
                      "is_enterprise": true,
                      "enterprise_reason": "direct-routing",
                      "contact_required": true,
                      "contact_message": "Direct Routing requires Session Border Controller setup and carrier integration. Our team designs custom solutions. Contact us for tailored pricing."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "Validation failed",
                  "details": ["users must be an integer between 1 and 9999."]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "example": 60
                },
                "description": "Seconds to wait before retrying."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "Rate limit exceeded"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Teams Phone"],
        "summary": "Calculate Microsoft Teams Phone estimate using a JSON body",
        "description": "Calculate using a JSON body. Returns an estimated project total, implementation duration and number porting timeline for supported Teams Phone migration scenarios. Rate limit: 10 requests per IP per minute.",
        "operationId": "calculateTeamsPhoneEstimate",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeamsPhoneQuoteInput"
              },
              "examples": {
                "smallCallingPlans": {
                  "summary": "25 users, 25 numbers, 1 auto attendant",
                  "value": {
                    "users": 25,
                    "numbers_to_port": 25,
                    "auto_attendants": 1,
                    "call_queues": 0,
                    "connectivity": "microsoft-calling-plans"
                  }
                },
                "operatorConnectMedium": {
                  "summary": "100 users with Operator Connect",
                  "value": {
                    "users": 100,
                    "numbers_to_port": 100,
                    "auto_attendants": 3,
                    "call_queues": 4,
                    "connectivity": "operator-connect"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Teams Phone estimate calculated successfully.",
            "headers": {
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer",
                  "example": 10
                },
                "description": "Maximum number of requests allowed per minute."
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer",
                  "example": 9
                },
                "description": "Requests remaining in the current minute window."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeamsPhoneQuoteOutput"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "example": 60
                },
                "description": "Seconds to wait before retrying."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "options": {
        "tags": ["Teams Phone"],
        "summary": "CORS preflight",
        "security": [],
        "responses": {
          "204": {
            "description": "CORS preflight accepted."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "TeamsPhoneQuoteInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "users",
          "numbers_to_port",
          "auto_attendants",
          "call_queues",
          "connectivity"
        ],
        "properties": {
          "users": {
            "type": "integer",
            "minimum": 1,
            "maximum": 9999,
            "description": "Number of users in scope. Values above 200 return an enterprise contact-required response.",
            "examples": [25]
          },
          "numbers_to_port": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1000,
            "description": "Count of existing phone numbers the customer intends to port to Teams Phone. Used for porting timeline estimate and one-time porting fees.",
            "examples": [25]
          },
          "numbers_ported": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1000,
            "deprecated": true,
            "description": "Deprecated alias for numbers_to_port. Accepted and normalized server-side; responses include a deprecation_warnings entry when used. Will be removed in v1.0.",
            "examples": [25]
          },
          "auto_attendants": {
            "type": "integer",
            "minimum": 0,
            "maximum": 50,
            "description": "Auto attendants to configure.",
            "examples": [1]
          },
          "call_queues": {
            "type": "integer",
            "minimum": 0,
            "maximum": 50,
            "description": "Call queues to configure.",
            "examples": [0]
          },
          "connectivity": {
            "type": "string",
            "description": "Teams Phone connectivity model.",
            "enum": [
              "microsoft-calling-plans",
              "operator-connect",
              "direct-routing"
            ],
            "examples": ["microsoft-calling-plans"]
          }
        }
      },
      "TeamsPhoneQuoteOutput": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/TeamsPhoneCalculatedQuoteOutput"
          },
          {
            "$ref": "#/components/schemas/TeamsPhoneEnterpriseQuoteOutput"
          }
        ]
      },
      "TeamsPhoneCalculatedQuoteOutput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "estimated_total_usd",
          "tier",
          "duration_weeks_min",
          "duration_weeks_max",
          "porting_timeline_weeks_min",
          "porting_timeline_weeks_max",
          "porting_timeline",
          "hypercare_days",
          "breakdown",
          "connectivity_model",
          "users_count",
          "is_enterprise",
          "contact_required",
          "contact_message"
        ],
        "properties": {
          "estimated_total_usd": {
            "type": "integer",
            "description": "Estimated total project cost in USD.",
            "examples": [4250]
          },
          "tier": {
            "type": "string",
            "enum": ["small", "mid", "large"],
            "description": "Project sizing tier based on numbers ported."
          },
          "duration_weeks_min": {
            "type": "integer",
            "description": "Minimum estimated project duration in weeks."
          },
          "duration_weeks_max": {
            "type": "integer",
            "description": "Maximum estimated project duration in weeks."
          },
          "porting_timeline_weeks_min": {
            "type": "integer",
            "const": 2,
            "description": "Minimum number porting timeline in weeks."
          },
          "porting_timeline_weeks_max": {
            "type": "integer",
            "const": 3,
            "description": "Maximum number porting timeline in weeks."
          },
          "porting_timeline": {
            "type": "string",
            "description": "Canonical porting timeline statement, identical across UI, API, and docs.",
            "examples": ["Porting timeline: typically 2 to 3 weeks after LOA submission, depending on losing carrier response time."]
          },
          "hypercare_days": {
            "type": "integer",
            "const": 30,
            "description": "Fixed 30-day hypercare period after cutover for all standard Teams Phone projects."
          },
          "breakdown": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "base",
              "number_porting",
              "auto_attendants",
              "call_queues",
              "connectivity_surcharge"
            ],
            "properties": {
              "base": {
                "type": "integer",
                "const": 1500,
                "description": "Base project cost."
              },
              "number_porting": {
                "type": "integer",
                "description": "Number porting cost."
              },
              "auto_attendants": {
                "type": "integer",
                "description": "Auto attendant configuration cost."
              },
              "call_queues": {
                "type": "integer",
                "description": "Call queue configuration cost."
              },
              "connectivity_surcharge": {
                "type": "integer",
                "description": "Connectivity model surcharge."
              }
            }
          },
          "connectivity_model": {
            "type": "string",
            "enum": [
              "microsoft-calling-plans",
              "operator-connect",
              "direct-routing"
            ]
          },
          "users_count": {
            "type": "integer",
            "description": "Input user count."
          },
          "is_enterprise": {
            "type": "boolean",
            "const": false
          },
          "contact_required": {
            "type": "boolean",
            "const": true,
            "description": "Formal quotes require contact with NeoDefender."
          },
          "contact_message": {
            "type": "string",
            "description": "Estimate disclaimer and formal quote guidance."
          }
        }
      },
      "TeamsPhoneEnterpriseQuoteOutput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "estimated_total_usd",
          "tier",
          "is_enterprise",
          "enterprise_reason",
          "contact_required",
          "contact_message"
        ],
        "properties": {
          "estimated_total_usd": {
            "type": "integer",
            "const": 0
          },
          "tier": {
            "type": "string",
            "const": "enterprise"
          },
          "is_enterprise": {
            "type": "boolean",
            "const": true
          },
          "enterprise_reason": {
            "type": "string",
            "enum": ["user-count", "direct-routing"],
            "description": "Reason the request requires custom planning."
          },
          "contact_required": {
            "type": "boolean",
            "const": true
          },
          "contact_message": {
            "type": "string",
            "description": "Custom planning guidance."
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": ["error"],
        "properties": {
          "error": {
            "type": "string",
            "description": "Generic error message."
          },
          "details": {
            "type": "array",
            "description": "Sanitized validation details.",
            "items": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}
