{
  "openapi": "3.1.0",
  "info": {
    "title": "Data Migration Tools API",
    "version": "1.0.0",
    "description": "Open, rate-limited HTTP API for the Data Migration Tools. Each endpoint runs a pure tool function against a JSON body and returns a ToolResult envelope. No authentication required; 30 requests per minute per IP; 2 MB body cap."
  },
  "servers": [
    {
      "url": "https://dmt-blog.pages.dev",
      "description": "Production"
    }
  ],
  "paths": {
    "/api/v1/tools/convert": {
      "post": {
        "operationId": "convert",
        "summary": "Convert text between CSV, JSON, XML, YAML, and SQL by pivoting through JSON when no direct converter exists.",
        "description": "The converted text, the row count from the last step that reported one, and the converter chain that ran.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "from": {
                    "type": "string",
                    "description": "Source format.",
                    "enum": [
                      "csv",
                      "json",
                      "xml",
                      "yaml",
                      "sql"
                    ]
                  },
                  "to": {
                    "type": "string",
                    "description": "Target format.",
                    "enum": [
                      "csv",
                      "json",
                      "xml",
                      "yaml",
                      "sql"
                    ]
                  },
                  "input": {
                    "type": "string",
                    "description": "The text to convert, in the source format."
                  },
                  "autoTypes": {
                    "type": "boolean",
                    "description": "Infer number/boolean/null types when parsing CSV rather than treating every cell as a string.",
                    "default": true
                  },
                  "dialect": {
                    "type": "string",
                    "description": "SQL dialect for CSV-to-SQL output. Ignored for other target formats.",
                    "enum": [
                      "mysql",
                      "postgres",
                      "sqlserver",
                      "sqlite"
                    ]
                  },
                  "table": {
                    "type": "string",
                    "description": "Table name for CSV-to-SQL INSERT statements. Ignored for other target formats.",
                    "default": "data"
                  },
                  "rootElement": {
                    "type": "string",
                    "description": "Root XML element name for JSON-to-XML output. Ignored for other target formats.",
                    "default": "root"
                  }
                },
                "required": [
                  "from",
                  "to",
                  "input"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/base64": {
      "post": {
        "operationId": "base64",
        "summary": "Encode a string to Base64 or decode Base64 back to text. Supports URL-safe (RFC 4648 §5) and PEM-style line-wrap on encode.",
        "description": "The encoded or decoded Base64 string, plus the byte count of the input.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "description": "Whether to encode or decode.",
                    "enum": [
                      "encode",
                      "decode"
                    ]
                  },
                  "input": {
                    "type": "string",
                    "description": "The text to encode, or the Base64 string to decode."
                  },
                  "urlSafe": {
                    "type": "boolean",
                    "description": "Produce (or accept) the URL-safe alphabet with - and _ instead of + and /.",
                    "default": false
                  },
                  "lineWrap": {
                    "type": "boolean",
                    "description": "On encode, wrap output into lines of lineWrapWidth characters (PEM-style).",
                    "default": false
                  },
                  "lineWrapWidth": {
                    "type": "number",
                    "description": "Line width when lineWrap is true. Default 76 (RFC 1421).",
                    "default": 76
                  }
                },
                "required": [
                  "action",
                  "input"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/jwt-decode": {
      "post": {
        "operationId": "jwt_decode",
        "summary": "Decode a JWT header and payload, describe registered claims, and report expiry status. Optionally verify an HS256 signature.",
        "description": "The decoded header and payload, registered-claim descriptions, expiry status, and (when verified) the signature check result.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "The JWT (three Base64URL segments separated by dots)."
                  },
                  "verify": {
                    "type": "object",
                    "properties": {
                      "secret": {
                        "type": "string",
                        "description": "Shared secret for HS256 signature verification. Omit to skip verification."
                      }
                    }
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/cron": {
      "post": {
        "operationId": "cron",
        "summary": "Parse a 5-field cron expression (or @-macro), describe it in English, and compute the next N run times.",
        "description": "A plain-English description of the schedule and the next run timestamps.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "expression": {
                    "type": "string",
                    "description": "A 5-field cron expression or an @-macro (@daily, @hourly, etc.)."
                  },
                  "runs": {
                    "type": "number",
                    "description": "How many next-run timestamps to return (1-100, default 5).",
                    "default": 5
                  },
                  "from": {
                    "type": "string",
                    "description": "ISO 8601 reference time to compute next runs from. Defaults to now."
                  }
                },
                "required": [
                  "expression"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/regex-test": {
      "post": {
        "operationId": "regex_test",
        "summary": "Test a JavaScript regex against a subject string. Returns all matches (capped at 10,000) with groups, named groups, and timing.",
        "description": "The match count, an array of matches with captured and named groups, and the elapsed time in milliseconds.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "pattern": {
                    "type": "string",
                    "description": "The regex pattern (without surrounding slashes)."
                  },
                  "flags": {
                    "type": "string",
                    "description": "Regex flags (e.g. \"g\", \"i\", \"u\"). Default empty."
                  },
                  "input": {
                    "type": "string",
                    "description": "The subject string to test against."
                  }
                },
                "required": [
                  "pattern",
                  "input"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/pii-scan": {
      "post": {
        "operationId": "pii_scan",
        "summary": "Scan CSV, JSON, or plain text for PII (emails, phones, SSNs, cards, IPs, IBANs, passports, URLs, names) with GDPR/CCPA risk flags.",
        "description": "A roll-up of findings per entity/column, plus optionally the per-span detail array when detailed is true.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "type": "string",
                    "description": "The data to scan."
                  },
                  "format": {
                    "type": "string",
                    "description": "How to interpret the input. CSV and JSON scan per-field; text scans the whole string.",
                    "enum": [
                      "csv",
                      "json",
                      "text"
                    ],
                    "default": "text"
                  },
                  "entities": {
                    "type": "array",
                    "description": "Restrict detection to these entity types (e.g. [\"EMAIL\",\"SSN\"]). Omit for all recognizers.",
                    "items": {
                      "type": "string"
                    }
                  },
                  "detectNames": {
                    "type": "boolean",
                    "description": "Also test whether whole fields are person names. Defaults to true for CSV/JSON, false for text."
                  },
                  "detailed": {
                    "type": "boolean",
                    "description": "Include the per-span detail array (offsets, values) alongside the roll-up.",
                    "default": false
                  }
                },
                "required": [
                  "input"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/pii-mask": {
      "post": {
        "operationId": "pii_mask",
        "summary": "Replace PII with shape-valid, deterministic fake values so masked exports still join and load. The real→fake mapping is returned separately.",
        "description": "The masked output, the real→fake entity mapping, and a count of masked values per entity.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "type": "string",
                    "description": "The data to mask."
                  },
                  "format": {
                    "type": "string",
                    "description": "How to interpret the input.",
                    "enum": [
                      "csv",
                      "json",
                      "text"
                    ],
                    "default": "text"
                  },
                  "entities": {
                    "type": "array",
                    "description": "Restrict masking to these entity types. Valid: EMAIL, SSN, CREDIT_CARD, PHONE, IPV4, IBAN, URL, PERSON_NAME, PASSPORT, DOB. Defaults to EMAIL, SSN, CREDIT_CARD, PHONE, IPV4, IBAN, URL, PERSON_NAME.",
                    "items": {
                      "type": "string"
                    }
                  },
                  "seed": {
                    "type": "string",
                    "description": "Deterministic seed for the pseudonymizer. Same seed + same input → same masked output. Defaults to \"dmt\"."
                  }
                },
                "required": [
                  "input"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/csv-validate": {
      "post": {
        "operationId": "csv_validate",
        "summary": "Validate CSV structure: duplicate headers, ragged rows, type inference, and per-column null/unique stats.",
        "description": "A validity flag, a list of structural issues, and per-column statistics (type, nulls, unique count).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "type": "string",
                    "description": "The CSV to validate."
                  }
                },
                "required": [
                  "input"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/data-clean": {
      "post": {
        "operationId": "data_clean",
        "summary": "Trim whitespace, drop empty rows and columns, normalize line endings, and rename duplicate headers in a CSV.",
        "description": "The cleaned CSV text and a summary of what was changed.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "type": "string",
                    "description": "The CSV to clean."
                  },
                  "options": {
                    "type": "object",
                    "properties": {
                      "trimWhitespace": {
                        "type": "boolean",
                        "description": "Trim leading/trailing whitespace in every cell.",
                        "default": true
                      },
                      "dropEmptyRows": {
                        "type": "boolean",
                        "description": "Drop rows where every cell is empty.",
                        "default": true
                      },
                      "dropEmptyColumns": {
                        "type": "boolean",
                        "description": "Drop columns where every cell is empty.",
                        "default": true
                      },
                      "normalizeLineEndings": {
                        "type": "boolean",
                        "description": "Normalize CRLF/CR to LF.",
                        "default": true
                      },
                      "deduplicateHeaders": {
                        "type": "boolean",
                        "description": "Rename duplicate header names (the second \"id\" becomes \"id_2\"). Set false to keep the names exactly as they appear in the file. Does not deduplicate rows.",
                        "default": true
                      }
                    }
                  }
                },
                "required": [
                  "input"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/schema-map": {
      "post": {
        "operationId": "schema_map",
        "summary": "Suggest field mappings between a source and target schema by name and type similarity.",
        "description": "An array of matches per source field: the target field, a 0-1 score, and the match method (exact/fuzzy/skip).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sourceFields": {
                    "type": "array",
                    "description": "Source schema fields, each { name, type }.",
                    "items": {
                      "type": "object"
                    }
                  },
                  "targetFields": {
                    "type": "array",
                    "description": "Target schema fields, each { name, type }.",
                    "items": {
                      "type": "object"
                    }
                  },
                  "threshold": {
                    "type": "number",
                    "description": "Minimum 0-1 score for a match to be accepted. Lower returns more (weaker) matches.",
                    "default": 0.4
                  }
                },
                "required": [
                  "sourceFields",
                  "targetFields"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/migration-validate": {
      "post": {
        "operationId": "migration_validate",
        "summary": "Reconcile source and target CSVs by a key column: matched count, only-in-source, only-in-target, and per-field diffs.",
        "description": "Matched/only-in-source/only-in-target counts, and a per-field diff of mismatched values.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "source": {
                    "type": "string",
                    "description": "The source CSV text."
                  },
                  "target": {
                    "type": "string",
                    "description": "The target CSV text."
                  },
                  "srcKey": {
                    "type": "string",
                    "description": "Column name to use as the key in the source. Defaults to the first header."
                  },
                  "tgtKey": {
                    "type": "string",
                    "description": "Column name to use as the key in the target. Defaults to the first header."
                  },
                  "mode": {
                    "type": "string",
                    "description": "Comparison mode: trim (whitespace-insensitive) or smart (also normalize case/quotes).",
                    "enum": [
                      "trim",
                      "smart"
                    ]
                  }
                },
                "required": [
                  "source",
                  "target"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/vendor-evaluate": {
      "post": {
        "operationId": "vendor_evaluate",
        "summary": "Score vendors against weighted criteria. Each category holds criteria scored 0-10 per vendor; the weighted sum is normalized to a 0-100 score with an A-F grade.",
        "description": "Per-vendor final score and grade, plus per-category averages for breakdown charts.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "vendors": {
                    "type": "array",
                    "description": "Array of { id, name, color? } objects (max 10).",
                    "items": {
                      "type": "object"
                    }
                  },
                  "categories": {
                    "type": "array",
                    "description": "Array of { id, name, weight, criteria: [{ id, name, scores: { vendorId: number } }] }.",
                    "items": {
                      "type": "object"
                    }
                  }
                },
                "required": [
                  "vendors",
                  "categories"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/roi-calculate": {
      "post": {
        "operationId": "roi_calculate",
        "summary": "Compute a 36-month cost-of-inaction vs migration NPV. Compares keeping the legacy stack (license + labor + data-error risk) against migrating to a new SaaS, with an overlap period and retained legacy cost.",
        "description": "Per-month cumulative cost arrays for legacy and migration curves, NPV of each, net savings NPV, breakeven month, and cost composition percentages.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "rev": {
                    "type": "number",
                    "description": "Annual revenue. Used to size the data-error risk cost."
                  },
                  "leg": {
                    "type": "number",
                    "description": "Legacy monthly license cost."
                  },
                  "hours": {
                    "type": "number",
                    "description": "Maintenance hours per week."
                  },
                  "rate": {
                    "type": "number",
                    "description": "Hourly labor cost."
                  },
                  "err": {
                    "type": "number",
                    "description": "Data error impact as a percentage of revenue (e.g. 1.5 = 1.5%)."
                  },
                  "mig": {
                    "type": "number",
                    "description": "One-time migration fee."
                  },
                  "newSaaS": {
                    "type": "number",
                    "description": "New SaaS monthly cost."
                  },
                  "dur": {
                    "type": "number",
                    "description": "Overlap duration in months (legacy + new SaaS run side by side)."
                  },
                  "growth": {
                    "type": "number",
                    "description": "Annual data growth as a percentage (e.g. 10 = 10%)."
                  },
                  "eff": {
                    "type": "number",
                    "description": "Operational efficiency as a percentage (e.g. 90 = 90% of labor eliminated)."
                  }
                },
                "required": [
                  "rev",
                  "leg",
                  "hours",
                  "rate",
                  "err",
                  "mig",
                  "newSaaS",
                  "dur",
                  "growth",
                  "eff"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/data-profile": {
      "post": {
        "operationId": "data_profile",
        "summary": "Profile CSV or JSON data: per-column type inference (Numeric/Date/Text/Mixed/Empty), unique and missing counts, and top 50 values.",
        "description": "Per-column metadata (type, unique count, missing count, top values) and the total row count.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "type": "string",
                    "description": "The data to profile. CSV text or a JSON array of objects."
                  },
                  "format": {
                    "type": "string",
                    "description": "How to interpret the input.",
                    "enum": [
                      "csv",
                      "json"
                    ],
                    "default": "csv"
                  }
                },
                "required": [
                  "input"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/helpdesk-evaluate": {
      "post": {
        "operationId": "helpdesk_evaluate",
        "summary": "Rank 23 helpdesk plans against required features. Returns a match score (0-100), missing features, annual cost, and per-category coverage for each plan.",
        "description": "An array of scored plans (match score, missing features, annual cost, category scores) and the total count.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "search": {
                    "type": "string",
                    "description": "Filter plans by brand name (case-insensitive substring)."
                  },
                  "budget": {
                    "type": "number",
                    "description": "Maximum cost per agent per month. Flat-fee plans always pass.",
                    "default": 200
                  },
                  "teamSize": {
                    "type": "number",
                    "description": "Number of agents, used to compute annual cost for per-agent plans.",
                    "default": 5
                  },
                  "filters": {
                    "type": "object",
                    "description": "Object of feature-key → boolean. Only keys set to true are required. See the docs page for the full feature key list."
                  },
                  "sort": {
                    "type": "string",
                    "description": "Sort order for the results.",
                    "enum": [
                      "match_desc",
                      "price_asc",
                      "price_desc",
                      "name"
                    ],
                    "default": "match_desc"
                  }
                }
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    },
    "/api/v1/tools/helpdesk-plan": {
      "post": {
        "operationId": "helpdesk_plan",
        "summary": "Estimate a helpdesk migration timeline: split entities into API vs manual tasks, compute throughput based on platform rate limits, and assess risk.",
        "description": "Total/min/max duration in hours, a breakdown (foundation/core data/attachments), per-entity hours, the bottleneck platform, risk level, and API vs manual task lists.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "source": {
                    "type": "string",
                    "description": "Source platform.",
                    "enum": [
                      "zendesk",
                      "freshdesk",
                      "salesforce",
                      "hubspot",
                      "intercom",
                      "helpscout",
                      "jira",
                      "front",
                      "gladly",
                      "gorgias"
                    ]
                  },
                  "sourcePlan": {
                    "type": "string",
                    "description": "Plan key on the source platform (e.g. \"suite_growth\")."
                  },
                  "destination": {
                    "type": "string",
                    "description": "Destination platform.",
                    "enum": [
                      "zendesk",
                      "freshdesk",
                      "salesforce",
                      "hubspot",
                      "intercom",
                      "helpscout",
                      "jira",
                      "front",
                      "gladly",
                      "gorgias"
                    ]
                  },
                  "destPlan": {
                    "type": "string",
                    "description": "Plan key on the destination platform."
                  },
                  "selectedEntities": {
                    "type": "array",
                    "description": "Entities to migrate. Valid: tickets, users, organizations, groups, articles, macros, triggers, automations, tags, custom_fields, ticket_forms, sla_policies.",
                    "items": {
                      "type": "string"
                    }
                  },
                  "volumes": {
                    "type": "object",
                    "description": "Record counts per entity, e.g. { \"tickets\": 50000, \"users\": 10000 }."
                  },
                  "avgAttachmentsPerTicket": {
                    "type": "number",
                    "description": "Average attachments per ticket.",
                    "default": 0
                  },
                  "avgAttachmentSizeMB": {
                    "type": "number",
                    "description": "Average attachment size in MB.",
                    "default": 0
                  }
                },
                "required": [
                  "source",
                  "sourcePlan",
                  "destination",
                  "destPlan",
                  "selectedEntities"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success — the tool ran and returned a ToolResult envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "output": {},
                    "meta": {
                      "type": "object"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (EMPTY_INPUT, INVALID_INPUT, PARSE_ERROR, UNSUPPORTED)."
          },
          "413": {
            "description": "Body too large (TOO_LARGE)."
          },
          "429": {
            "description": "Rate limit exceeded (RATE_LIMIT)."
          },
          "500": {
            "description": "Internal error (INTERNAL)."
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "ToolResult": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "output": {},
          "meta": {
            "type": "object"
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "EMPTY_INPUT",
              "TOO_LARGE",
              "PARSE_ERROR",
              "INVALID_INPUT",
              "UNSUPPORTED",
              "TIMEOUT",
              "DEPENDENCY_MISSING",
              "RATE_LIMIT",
              "INTERNAL"
            ]
          }
        }
      }
    }
  }
}