{
  "openapi": "3.1.0",
  "info": {
    "title": "QuassLabs API",
    "version": "0.1.0",
    "summary": "Deterministic developer and delivery tools, callable over REST or MCP.",
    "description": "A set of small, deterministic HTTP endpoints for AI builders and delivery teams - prompt safety grading, AI-writing detection and rewriting, model selection and cost, diagram accessibility QA, dependency hygiene, log analysis, and agile/QA linting. Almost every endpoint is pure computation: no LLM in the request path, so results are reproducible and cheap.\n\nEvery endpoint is also an MCP tool. Point an MCP client at `/mcp` and the same surface appears natively inside Claude, Claude Code, or any other MCP host - with the same key, the same rate limits, and the same credit metering.\n\n### Authentication\n\nSend `Authorization: Bearer qlk_YOUR_API_KEY` on every request. Keys are stored only as a SHA-256 hash, so a lost key cannot be recovered - revoke and reissue it.\n\n### Credits\n\nEach successful call decrements your key's credit balance by that endpoint's cost (shown per operation below, and in `x-credit-cost`). The decrement is atomic and conditional: if the balance is short you get `402 insufficient_credits` and nothing runs. Rate limits are checked *before* credits, so a `429` is never billed, and any call that fails with a 5xx is refunded automatically.\n\n### Errors\n\nEvery non-2xx response is `{\"error\": \"<code>\"}`, sometimes with extra fields. Branch on `error`, not on the status code alone.",
    "contact": {
      "name": "QuassLabs",
      "url": "https://quasslabs.com"
    }
  },
  "servers": [
    {
      "url": "https://api.quasslabs.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Endpoints",
      "description": "The metered tool endpoints."
    },
    {
      "name": "MCP",
      "description": "Model Context Protocol transport."
    },
    {
      "name": "Administration",
      "description": "Operator-only key management."
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Send your key as `Authorization: Bearer qlk_...`. Keys are opaque; only a SHA-256 hash is stored server-side, so a lost key cannot be recovered - it can only be revoked and reissued."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "description": "Every non-2xx response from every endpoint uses this shape.",
        "properties": {
          "error": {
            "type": "string",
            "description": "Stable machine-readable error code. Match on this, not on the HTTP status alone."
          },
          "request_id": {
            "type": "string",
            "description": "Present once the call has been metered. Quote it in support requests."
          },
          "required_scope": {
            "type": "string",
            "description": "On insufficient_scope: the scope your key is missing."
          },
          "limit": {
            "type": "integer",
            "description": "On rate_limit_exceeded: calls permitted per window."
          },
          "window_seconds": {
            "type": "integer",
            "description": "On rate_limit_exceeded: length of the rate-limit window in seconds."
          },
          "retry_after": {
            "type": "integer",
            "description": "On rate_limit_exceeded or rate_limiter_unavailable: seconds to wait before retrying."
          },
          "allowed": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "On a value-not-permitted error: the accepted values."
          }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "description": "A single JSON-RPC 2.0 request. An array of these is also accepted.",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0",
            "description": "Protocol version. Must be \"2.0\"."
          },
          "id": {
            "description": "Request identifier, echoed on the response. Omit for notifications.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              }
            ]
          },
          "method": {
            "type": "string",
            "enum": [
              "initialize",
              "notifications/initialized",
              "ping",
              "tools/list",
              "tools/call"
            ],
            "description": "The MCP method to invoke."
          },
          "params": {
            "type": "object",
            "additionalProperties": true,
            "description": "Method parameters. For tools/call: {name, arguments}."
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": [
          "jsonrpc"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0",
            "description": "Always \"2.0\"."
          },
          "id": {
            "description": "Echoes the request id, or null when the request could not be parsed.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "result": {
            "type": "object",
            "additionalProperties": true,
            "description": "Method result. For tools/call: {content: [{type, text}], isError}."
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "description": "Protocol-level error. Tool failures use result.isError instead.",
            "properties": {
              "code": {
                "type": "integer",
                "description": "JSON-RPC error code, e.g. -32601 for an unknown method.",
                "example": -32601
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation."
              }
            }
          }
        }
      },
      "acceptance_criteria_validator_problem": {
        "type": "object",
        "description": "One defect found in a single acceptance criterion.",
        "required": [
          "rule",
          "detail"
        ],
        "properties": {
          "rule": {
            "type": "string",
            "enum": [
              "observable_outcome",
              "testable",
              "atomic"
            ],
            "description": "Which quality rule failed."
          },
          "detail": {
            "type": "string",
            "description": "What is wrong and how to fix it."
          },
          "vague_terms": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Present on \"testable\" problems only: the subjective words that triggered it.",
            "example": [
              "fast",
              "user-friendly"
            ]
          }
        }
      },
      "acceptance_criteria_validator_criterion": {
        "type": "object",
        "description": "The evaluation of one acceptance criterion.",
        "required": [
          "index",
          "ac",
          "problems",
          "checks",
          "ac_score_0_100"
        ],
        "properties": {
          "index": {
            "type": "integer",
            "minimum": 0,
            "description": "Position in the submitted list."
          },
          "ac": {
            "type": "string",
            "description": "The acceptance criterion text."
          },
          "problems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/acceptance_criteria_validator_problem"
            },
            "description": "Defects found. Empty when the AC is clean."
          },
          "checks": {
            "type": "object",
            "description": "Pass/fail for each rule, always all four, so a caller can show a checklist rather than only failures.",
            "required": [
              "observable_outcome",
              "testable",
              "atomic",
              "clear_condition"
            ],
            "additionalProperties": false,
            "properties": {
              "observable_outcome": {
                "type": "boolean",
                "description": "States a result someone could observe. Worth 45 points."
              },
              "testable": {
                "type": "boolean",
                "description": "Free of vague or unmeasurable wording. Worth 35 points."
              },
              "atomic": {
                "type": "boolean",
                "description": "Covers exactly one requirement. Worth 25 points."
              },
              "clear_condition": {
                "type": "boolean",
                "description": "Has an explicit condition-to-outcome shape. A 5-point bonus, not a problem when absent."
              }
            }
          },
          "ac_score_0_100": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "This criterion alone, starting at 100 with a deduction per failed check."
          }
        }
      },
      "defect_triage_triaged": {
        "type": "object",
        "description": "One triaged bug report.",
        "required": [
          "severity",
          "category",
          "has_repro",
          "missing_info",
          "priority"
        ],
        "properties": {
          "severity": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low"
            ],
            "description": "Inferred from keyword signals in the report text."
          },
          "category": {
            "type": "string",
            "enum": [
              "security",
              "data",
              "crash",
              "performance",
              "integration",
              "ui",
              "docs",
              "other"
            ],
            "description": "First matching category by keyword; \"other\" when nothing matches."
          },
          "has_repro": {
            "type": "boolean",
            "description": "Whether the report appears to contain reproduction steps."
          },
          "missing_info": {
            "type": "array",
            "description": "What the report needs before it is actionable. Empty when nothing is missing.",
            "items": {
              "type": "string",
              "enum": [
                "no_repro_steps",
                "no_expected_vs_actual",
                "no_environment"
              ],
              "description": "A specific piece of absent information."
            }
          },
          "priority": {
            "type": "integer",
            "minimum": 1,
            "description": "Rank within the batch, 1 being most urgent. Always 1 for a single report."
          }
        }
      },
      "diagram_contrast_failure": {
        "type": "object",
        "description": "A label/background pair that fails WCAG AA, with both colours resolved.",
        "required": [
          "fg",
          "bg",
          "ratio",
          "needed",
          "label"
        ],
        "properties": {
          "fg": {
            "type": "string",
            "description": "Label colour."
          },
          "bg": {
            "type": "string",
            "description": "Resolved background colour."
          },
          "ratio": {
            "type": "number",
            "description": "Contrast ratio achieved.",
            "example": 2.1
          },
          "needed": {
            "type": "number",
            "description": "Ratio required to pass.",
            "example": 4.5
          },
          "label": {
            "type": "string",
            "description": "Which element failed."
          }
        }
      },
      "diagram_contrast_unverifiedFailure": {
        "type": "object",
        "description": "A node whose background could not be determined statically, so no honest verdict is possible.",
        "required": [
          "fg",
          "bg",
          "ratio",
          "needed",
          "label",
          "unverified",
          "reason"
        ],
        "properties": {
          "fg": {
            "type": "string",
            "description": "Label colour, which was resolvable."
          },
          "bg": {
            "type": "null",
            "description": "Always null - this is the value that could not be resolved."
          },
          "ratio": {
            "type": "null",
            "description": "Always null, since no ratio can be computed without a background."
          },
          "needed": {
            "type": "number",
            "description": "Ratio that would have been required."
          },
          "label": {
            "type": "string",
            "description": "Which element could not be verified."
          },
          "unverified": {
            "type": "boolean",
            "const": true,
            "description": "Marks this entry as unverified rather than failed."
          },
          "reason": {
            "type": "string",
            "description": "Why resolution failed, e.g. a gradient or url() fill."
          }
        }
      },
      "diagram_contrast_fix": {
        "type": "object",
        "description": "A proposed correction for one resolvable failure. Carries every field of the original failure plus the suggestion.",
        "required": [
          "fg",
          "bg",
          "ratio",
          "needed",
          "label",
          "suggested_fg",
          "fixed_ratio"
        ],
        "properties": {
          "fg": {
            "type": "string",
            "description": "Original label colour."
          },
          "bg": {
            "type": "string",
            "description": "Original background colour."
          },
          "ratio": {
            "type": "number",
            "description": "Original failing ratio."
          },
          "needed": {
            "type": "number",
            "description": "Ratio required."
          },
          "label": {
            "type": "string",
            "description": "Which element this fixes."
          },
          "suggested_fg": {
            "type": "string",
            "description": "Replacement label colour."
          },
          "suggested_bg": {
            "type": "string",
            "description": "Replacement background. Present only when the original fill could not be rescued by any label colour."
          },
          "fill_unrescuable": {
            "type": "boolean",
            "const": true,
            "description": "Present only when the fill itself had to change. When set, fixed_ratio refers to the NEW background, not the one you submitted."
          },
          "fixed_ratio": {
            "type": "number",
            "description": "Ratio after applying the suggestion.",
            "example": 7.2
          }
        }
      },
      "diagram_contrast_hardenChange": {
        "type": "object",
        "description": "One style or classDef line rewritten by harden mode.",
        "required": [
          "old_fill",
          "fill",
          "old_color",
          "color",
          "ratio",
          "fill_remapped"
        ],
        "properties": {
          "old_fill": {
            "type": "string",
            "description": "Fill before the rewrite."
          },
          "fill": {
            "type": "string",
            "description": "Fill after the rewrite. Unchanged unless fill_remapped is true."
          },
          "old_color": {
            "type": [
              "string",
              "null"
            ],
            "description": "Label colour before the rewrite, or null when the line declared none."
          },
          "color": {
            "type": "string",
            "description": "Label colour after the rewrite."
          },
          "ratio": {
            "type": "number",
            "description": "Contrast ratio of the rewritten pair."
          },
          "fill_remapped": {
            "type": "boolean",
            "description": "True when the fill itself had to be replaced because no label colour could reach the threshold against the original."
          }
        }
      },
      "log_analyzer_cluster": {
        "type": "object",
        "description": "One normalised error signature and how often it occurred.",
        "required": [
          "signature",
          "count",
          "level",
          "first_seen",
          "last_seen",
          "sample"
        ],
        "properties": {
          "signature": {
            "type": "string",
            "description": "The normalised message, with variable parts collapsed into placeholders."
          },
          "count": {
            "type": "integer",
            "minimum": 1,
            "description": "Occurrences of this signature."
          },
          "level": {
            "type": "string",
            "description": "Log level this signature appeared at.",
            "example": "ERROR"
          },
          "first_seen": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "First occurrence, or null when no line in the cluster carried a timestamp."
          },
          "last_seen": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Last occurrence, or null if untimestamped."
          },
          "sample": {
            "type": "string",
            "description": "One verbatim line from the cluster, so you can see the real message."
          }
        }
      },
      "model_advisor_alternative": {
        "type": "object",
        "description": "A model considered but not chosen as primary.",
        "required": [
          "model",
          "provider",
          "as_of",
          "reason"
        ],
        "properties": {
          "model": {
            "type": "string",
            "description": "Model identifier."
          },
          "provider": {
            "type": "string",
            "description": "Who serves it."
          },
          "as_of": {
            "type": "string",
            "description": "Date this entry was last priced."
          },
          "reason": {
            "type": "string",
            "description": "Why it ranked where it did."
          }
        }
      },
      "saas_metrics_healthTag": {
        "type": "object",
        "description": "A benchmark verdict plus the reasoning behind it.",
        "required": [
          "status",
          "message"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "excellent",
              "healthy",
              "pass",
              "borderline",
              "concerning",
              "unhealthy",
              "fail",
              "unknown"
            ],
            "description": "Severity band. \"unknown\" means the inputs needed were not supplied, not that the metric is bad. Rule-of-40 uses pass/fail where the others use healthy/unhealthy."
          },
          "message": {
            "type": "string",
            "description": "Plain-English explanation, including the computed value and the benchmark it is being judged against."
          }
        }
      },
      "sprint_health_metric": {
        "type": "object",
        "description": "A computed metric with its benchmark verdict and an explanation naming the thresholds used.",
        "required": [
          "value",
          "health",
          "note"
        ],
        "properties": {
          "value": {
            "type": "object",
            "description": "The computed numbers. Shape varies per metric."
          },
          "health": {
            "type": "string",
            "enum": [
              "good",
              "warn",
              "bad"
            ],
            "description": "Worst sub-check wins - a metric with one bad component is bad overall."
          },
          "note": {
            "type": "string",
            "description": "Explanation including the computed value and the thresholds it was judged against."
          }
        }
      },
      "sprint_health_ratioValue": {
        "type": "object",
        "description": "Per-sprint ratios plus their average.",
        "required": [
          "per_sprint",
          "avg"
        ],
        "properties": {
          "per_sprint": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "description": "The ratio for each sprint, oldest first."
          },
          "avg": {
            "type": "number",
            "description": "Mean across sprints."
          }
        }
      },
      "ticket_lint_finding": {
        "type": "object",
        "description": "One quality problem on one ticket.",
        "required": [
          "field",
          "severity",
          "code",
          "message",
          "fix"
        ],
        "properties": {
          "field": {
            "type": "string",
            "enum": [
              "summary",
              "description",
              "acceptance_criteria",
              "estimate",
              "priority",
              "epic_link",
              "assignee",
              "labels",
              "components",
              "attachments_count"
            ],
            "description": "Which part of the ticket is at fault."
          },
          "severity": {
            "type": "string",
            "enum": [
              "high",
              "medium",
              "low"
            ],
            "description": "High findings block a \"ready\" verdict outright and cost 20 points; medium costs 8, low costs 3."
          },
          "code": {
            "type": "string",
            "enum": [
              "summary_missing",
              "summary_too_short",
              "summary_too_long",
              "summary_all_caps",
              "summary_weak_start",
              "summary_trailing_ellipsis",
              "description_missing",
              "description_too_short",
              "no_user_value",
              "epic_no_child_scope",
              "bug_no_repro",
              "bug_no_expected_actual",
              "bug_no_environment",
              "bug_no_attachment",
              "ac_missing",
              "estimate_missing",
              "estimate_oversized",
              "estimate_mismatch",
              "priority_missing",
              "urgent_thin_context",
              "epic_link_missing",
              "assignee_missing",
              "labels_empty",
              "components_empty"
            ],
            "description": "Stable identifier for the rule that fired. Branch on this, not on the message text. Note estimate_missing can be high or medium depending on issue type."
          },
          "message": {
            "type": "string",
            "description": "What is wrong."
          },
          "fix": {
            "type": "string",
            "description": "What to do about it."
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Malformed request. The body was not valid JSON, or a field failed validation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "invalid_json": {
                "value": {
                  "error": "invalid_json"
                }
              },
              "validation": {
                "summary": "A required field was missing or malformed",
                "value": {
                  "error": "system_prompt_required"
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Authentication failed. No credits are consumed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "missing_token": {
                "summary": "No Authorization header, or not a Bearer header",
                "value": {
                  "error": "missing_token"
                }
              },
              "invalid_token": {
                "summary": "Not a qlk_ key, or no such key",
                "value": {
                  "error": "invalid_token"
                }
              },
              "key_revoked": {
                "summary": "The key exists but is no longer active",
                "value": {
                  "error": "key_revoked"
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key is valid but lacks the scope this endpoint requires.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "insufficient_scope": {
                "value": {
                  "error": "insufficient_scope",
                  "required_scope": "admin"
                }
              }
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "The key does not have enough credits for this call. The decrement is conditional and atomic, so nothing was charged and nothing was run.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "insufficient_credits": {
                "value": {
                  "error": "insufficient_credits"
                }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Per-key rate limit exceeded. Checked before credits are spent, so a throttled call is never billed. Honour the Retry-After header.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "rate_limit_exceeded": {
                "value": {
                  "error": "rate_limit_exceeded",
                  "limit": 60,
                  "window_seconds": 60,
                  "retry_after": 23
                }
              }
            }
          }
        }
      },
      "Unavailable": {
        "description": "The endpoint is administratively disabled, or the rate limiter is unreachable while configured to fail closed. Retry later; nothing was charged.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "endpoint_disabled": {
                "value": {
                  "error": "endpoint_disabled"
                }
              },
              "rate_limiter_unavailable": {
                "value": {
                  "error": "rate_limiter_unavailable",
                  "retry_after": 30
                }
              }
            }
          }
        }
      },
      "ServerError": {
        "description": "Unexpected server-side failure. Credits spent on the call are refunded automatically - a 5xx is never your bill.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "internal_error": {
                "value": {
                  "error": "internal_error"
                }
              }
            }
          }
        }
      }
    }
  },
  "paths": {
    "/v1/acceptance-criteria-validator": {
      "post": {
        "operationId": "acceptance_criteria_validator",
        "summary": "QA advisor: validate acceptance criteria quality",
        "description": "QA advisor: validate acceptance criteria quality. POST a list of AC (or a story) and get per-AC problems (vague/untestable, non-atomic, no observable outcome) + a missing-coverage flag if there is no failure-path AC, plus a 0-100 score and pass/needs_work verdict. Tells you exactly what to fix. Deterministic, no LLM.\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "acceptance_criteria_validator",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "acceptance_criteria": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "story": {
                    "type": "string"
                  },
                  "strictness": {
                    "type": "string",
                    "enum": [
                      "normal",
                      "strict"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ac_count",
                    "verdict",
                    "score_0_100",
                    "issues",
                    "criteria",
                    "missing_coverage",
                    "request_id"
                  ],
                  "properties": {
                    "ac_count": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "How many acceptance criteria were evaluated, after any extraction from a free-text story."
                    },
                    "verdict": {
                      "type": "string",
                      "enum": [
                        "pass",
                        "needs_work"
                      ],
                      "description": "Passes only when no AC has a problem AND no set-level coverage gap was found."
                    },
                    "score_0_100": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 100,
                      "example": 72,
                      "description": "Mean of the per-AC scores, then 15 points deducted per set-level coverage gap."
                    },
                    "criteria": {
                      "type": "array",
                      "description": "Full per-AC result, in submission order. One entry per criterion including the ones that passed cleanly.",
                      "items": {
                        "$ref": "#/components/schemas/acceptance_criteria_validator_criterion"
                      }
                    },
                    "issues": {
                      "type": "array",
                      "description": "The subset of criteria that have at least one problem. A convenience view of `criteria` - nothing here is absent from there.",
                      "items": {
                        "type": "object",
                        "required": [
                          "index",
                          "ac",
                          "problems"
                        ],
                        "properties": {
                          "index": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Position of this AC in the submitted list."
                          },
                          "ac": {
                            "type": "string",
                            "description": "The acceptance criterion text."
                          },
                          "problems": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/acceptance_criteria_validator_problem"
                            },
                            "description": "What is wrong with it."
                          }
                        }
                      }
                    },
                    "missing_coverage": {
                      "type": "array",
                      "description": "Gaps in the set as a whole rather than in any single AC. Empty when the set is complete.",
                      "items": {
                        "type": "object",
                        "required": [
                          "rule",
                          "detail"
                        ],
                        "properties": {
                          "rule": {
                            "type": "string",
                            "enum": [
                              "negative_coverage",
                              "empty_set"
                            ],
                            "description": "\"negative_coverage\" means no AC covers a failure or edge case; \"empty_set\" means nothing was submitted."
                          },
                          "detail": {
                            "type": "string",
                            "description": "Explanation and what to add."
                          }
                        }
                      }
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/alignment-gate": {
      "post": {
        "operationId": "alignment_gate",
        "summary": "Run an AI system prompt (and optional agent config) through a 45-criterion alignment/safety rubric",
        "description": "Run an AI system prompt (and optional agent config) through a 45-criterion alignment/safety rubric. Returns a letter grade (A-F), numeric score, per-flag remediation, and a pass/fail verdict.\n\nCosts **2 credits** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "alignment_gate",
        "x-credit-cost": 2,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "system_prompt"
                ],
                "properties": {
                  "system_prompt": {
                    "type": "string",
                    "description": "The system prompt text to evaluate."
                  },
                  "agent_config": {
                    "type": "object",
                    "description": "Optional agent config (tools, memory, temperature, etc.)."
                  },
                  "model_id": {
                    "type": "string",
                    "description": "Optional target model identifier."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "grade",
                    "score",
                    "flags",
                    "passed",
                    "summary",
                    "request_id"
                  ],
                  "properties": {
                    "grade": {
                      "type": "string",
                      "enum": [
                        "A",
                        "B",
                        "C",
                        "D",
                        "F"
                      ],
                      "description": "Letter grade. Any critical flag forces F and any high flag forces at best D, regardless of the numeric score."
                    },
                    "score": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 100,
                      "example": 85,
                      "description": "Starts at 100 and deducts per flag: 30 for critical, 15 for high, 7 for medium, 3 for low. Floored at 0."
                    },
                    "passed": {
                      "type": "boolean",
                      "description": "True only when the score is at least 70 AND no critical flag was raised. A prompt can score above 70 and still fail on a single critical."
                    },
                    "summary": {
                      "type": "string",
                      "maxLength": 2000,
                      "description": "Narrative assessment of the prompt. Falls back to \"No summary produced.\" rather than being empty."
                    },
                    "flags": {
                      "type": "array",
                      "maxItems": 20,
                      "description": "Individual findings, capped at 20. Malformed findings are dropped rather than failing the call, so this may be shorter than what the underlying model reported.",
                      "items": {
                        "type": "object",
                        "required": [
                          "category",
                          "severity",
                          "description",
                          "line_reference",
                          "remediation"
                        ],
                        "properties": {
                          "category": {
                            "type": "string",
                            "enum": [
                              "safety_harm",
                              "honesty_transparency",
                              "alignment_clarity",
                              "privacy_data",
                              "bias_fairness",
                              "robustness_jailbreak"
                            ],
                            "description": "Which of the six rubric areas this finding belongs to."
                          },
                          "severity": {
                            "type": "string",
                            "enum": [
                              "critical",
                              "high",
                              "medium",
                              "low"
                            ],
                            "description": "How serious the finding is. Drives both the deduction and the grade floor."
                          },
                          "description": {
                            "type": "string",
                            "maxLength": 1000,
                            "description": "What is wrong."
                          },
                          "line_reference": {
                            "type": "string",
                            "maxLength": 200,
                            "description": "Where in the submitted prompt it occurs. May be an empty string when the finding is about the prompt as a whole."
                          },
                          "remediation": {
                            "type": "string",
                            "maxLength": 1000,
                            "description": "How to fix it."
                          }
                        }
                      }
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/blm-land-lookup": {
      "post": {
        "operationId": "blm_land_lookup",
        "summary": "Look up Bureau of Land Management land status for a US coordinate: BLM ownership, jurisdiction, allowed activities (rockhounding, camping), permit requirements, and special designations (ACEC, Wilderness, WSA, NM/NCA)",
        "description": "Look up Bureau of Land Management land status for a US coordinate: BLM ownership, jurisdiction, allowed activities (rockhounding, camping), permit requirements, and special designations (ACEC, Wilderness, WSA, NM/NCA).\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "blm_land_lookup",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "lat",
                  "lon"
                ],
                "properties": {
                  "lat": {
                    "type": "number",
                    "description": "Latitude, -90 to 90."
                  },
                  "lon": {
                    "type": "number",
                    "description": "Longitude, -180 to 180."
                  },
                  "radius_km": {
                    "type": "number",
                    "description": "Optional buffer radius in km (0-50, default 1.0)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "is_blm_land",
                    "jurisdiction",
                    "allowed_activities",
                    "permit_requirements",
                    "special_designations",
                    "data_as_of",
                    "cache_hit",
                    "sources",
                    "request_id"
                  ],
                  "properties": {
                    "is_blm_land": {
                      "type": "boolean",
                      "description": "True when at least one BLM-administered parcel was found at the coordinate."
                    },
                    "jurisdiction": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "The governing land unit. Null when no surface-management record covers the coordinate at all, in which case the activity and permit lists are empty.",
                      "required": [
                        "agency",
                        "agency_code",
                        "dept_code",
                        "admin_unit_name",
                        "admin_unit_type",
                        "state"
                      ],
                      "properties": {
                        "agency": {
                          "type": "string",
                          "description": "Human-readable agency name.",
                          "example": "Bureau of Land Management"
                        },
                        "agency_code": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Agency code as published by the source layer.",
                          "example": "BLM"
                        },
                        "dept_code": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Parent department code.",
                          "example": "DOI"
                        },
                        "admin_unit_name": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Name of the administering unit, typically the field office."
                        },
                        "admin_unit_type": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Kind of administering unit."
                        },
                        "state": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Two-letter state code of the administering unit.",
                          "example": "UT"
                        }
                      }
                    },
                    "allowed_activities": {
                      "type": "array",
                      "description": "What you may do here, from a static rule table keyed by resolved land type. Approximate - see accuracy_note.",
                      "items": {
                        "type": "object",
                        "required": [
                          "activity",
                          "allowed",
                          "notes"
                        ],
                        "properties": {
                          "activity": {
                            "type": "string",
                            "enum": [
                              "rockhounding",
                              "primitive_camping",
                              "campfire",
                              "ohv"
                            ],
                            "description": "Which activity this rule covers."
                          },
                          "allowed": {
                            "oneOf": [
                              {
                                "type": "boolean"
                              },
                              {
                                "type": "string",
                                "const": "limited"
                              }
                            ],
                            "description": "true, false, or the string \"limited\" when it is permitted only under conditions described in notes. Test for the string explicitly - \"limited\" is truthy in most languages."
                          },
                          "notes": {
                            "type": "string",
                            "description": "Conditions, limits and caveats."
                          }
                        }
                      }
                    },
                    "permit_requirements": {
                      "type": "array",
                      "description": "Permits needed for particular activities on this land type. Empty when none apply.",
                      "items": {
                        "type": "object",
                        "required": [
                          "activity",
                          "permit_required",
                          "permit_type",
                          "notes"
                        ],
                        "properties": {
                          "activity": {
                            "type": "string",
                            "description": "The activity requiring a permit.",
                            "example": "overnight_group"
                          },
                          "permit_required": {
                            "type": "boolean",
                            "description": "Whether a permit is mandatory."
                          },
                          "permit_type": {
                            "type": "string",
                            "description": "Name of the permit.",
                            "example": "Wilderness permit"
                          },
                          "notes": {
                            "type": "string",
                            "description": "Thresholds and conditions, e.g. group size."
                          }
                        }
                      }
                    },
                    "special_designations": {
                      "type": "array",
                      "description": "Protective overlays at this location. Present only on BLM land; an overlay usually tightens the activity rules above.",
                      "items": {
                        "type": "object",
                        "required": [
                          "type",
                          "name",
                          "notes"
                        ],
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "ACEC",
                              "Wilderness",
                              "WSA",
                              "NM_NCA"
                            ],
                            "description": "Area of Critical Environmental Concern, designated Wilderness, Wilderness Study Area, or National Monument / National Conservation Area."
                          },
                          "name": {
                            "type": "string",
                            "description": "Name of the designated area."
                          },
                          "notes": {
                            "type": "string",
                            "description": "Standing caveat to verify current restrictions."
                          }
                        }
                      }
                    },
                    "blm_coverage_pct": {
                      "type": "number",
                      "minimum": 0,
                      "maximum": 100,
                      "description": "Share of surface-management records in the search area that are BLM. Present only when radius_km is greater than 0, and an envelope-level approximation rather than a true geometry intersection.",
                      "example": 66.7
                    },
                    "data_as_of": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the underlying data was queried. On a cache hit this is the ORIGINAL query time, not the time of your request."
                    },
                    "cache_hit": {
                      "type": "boolean",
                      "description": "True when served from the 30-day cache. You are charged either way."
                    },
                    "accuracy_note": {
                      "type": "string",
                      "description": "Standing caveat that activity and permit rules are approximate. Absent only when no jurisdiction was found."
                    },
                    "sources": {
                      "type": "object",
                      "description": "Which upstream datasets produced this answer.",
                      "required": [
                        "sma",
                        "special_designations"
                      ],
                      "properties": {
                        "sma": {
                          "type": "string",
                          "description": "Surface management agency layer."
                        },
                        "special_designations": {
                          "type": "string",
                          "description": "Conservation-system and ACEC layers."
                        }
                      }
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "504": {
            "description": "The upstream BLM ArcGIS service did not respond. Retry shortly; the credit is not refunded automatically at this status, but no data was returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "upstream_unavailable": {
                    "value": {
                      "error": "upstream_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/coverage-check": {
      "post": {
        "operationId": "coverage_check",
        "summary": "Scope-completeness advisor",
        "description": "Scope-completeness advisor. POST a brief (or requirements[]) + your stories[] and get a coverage score + the gaps (requirements no story covers), orphan stories (possible scope creep), and missing structural buckets (auth/CRUD/error-states/tests). Tells you what the scope is missing. Deterministic, no LLM.\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "coverage_check",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "stories"
                ],
                "properties": {
                  "brief": {
                    "type": "string"
                  },
                  "requirements": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "stories": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "coverage_score_0_100",
                    "covered",
                    "gaps",
                    "orphans",
                    "missing_buckets",
                    "total_requirements",
                    "total_stories",
                    "verdict",
                    "request_id"
                  ],
                  "properties": {
                    "coverage_score_0_100": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 100,
                      "example": 78,
                      "description": "Traceability is 80 percent of the score and structural buckets the remaining 20; each missing bucket costs 5 points."
                    },
                    "verdict": {
                      "type": "string",
                      "enum": [
                        "PASS",
                        "NEEDS REVISION"
                      ],
                      "description": "PASS requires a score of at least 85 AND zero gaps AND zero missing buckets. A single uncovered requirement blocks a pass regardless of score - a whole missing feature must not average away."
                    },
                    "total_requirements": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Requirements evaluated, whether supplied directly or extracted from the brief."
                    },
                    "total_stories": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Stories evaluated."
                    },
                    "covered": {
                      "type": "array",
                      "description": "Requirements matched to a story, with the match evidence.",
                      "items": {
                        "type": "object",
                        "required": [
                          "requirement",
                          "matched_story",
                          "score"
                        ],
                        "properties": {
                          "requirement": {
                            "type": "string",
                            "description": "The requirement text."
                          },
                          "matched_story": {
                            "type": "string",
                            "description": "The story that best covers it."
                          },
                          "score": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 1,
                            "example": 0.75,
                            "description": "Fraction of the requirement token set present in the story. A requirement is considered covered at 0.5 or above."
                          }
                        }
                      }
                    },
                    "gaps": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Requirements no story covers. These are the missing work items."
                    },
                    "orphans": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Stories that matched no requirement. Often scope creep, sometimes a requirement nobody wrote down."
                    },
                    "missing_buckets": {
                      "type": "array",
                      "description": "Structural areas absent from the story set entirely.",
                      "items": {
                        "type": "string",
                        "enum": [
                          "auth/account",
                          "CRUD",
                          "error-states",
                          "tests"
                        ],
                        "description": "A whole category of work with no story mentioning it."
                      }
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/defect-triage": {
      "post": {
        "operationId": "defect_triage",
        "summary": "Bug-triage advisor",
        "description": "Bug-triage advisor. POST one bug report (or a list) and get severity (critical/high/medium/low), category, whether it has repro steps, and what info is missing (no repro / no expected-vs-actual / no environment). For a list: priority ranking + likely-duplicate clusters. Deterministic, no LLM.\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "defect_triage",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "report": {
                    "type": "string"
                  },
                  "reports": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "description": "The shape depends on the input: a single `report` returns one triaged record under `result`, while a `reports` list returns `results` plus cross-report ranking and duplicate detection.",
                  "oneOf": [
                    {
                      "type": "object",
                      "title": "Single report",
                      "required": [
                        "result",
                        "request_id"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "result": {
                          "$ref": "#/components/schemas/defect_triage_triaged"
                        },
                        "request_id": {
                          "type": "string",
                          "description": "Identifier for this metered call."
                        }
                      }
                    },
                    {
                      "type": "object",
                      "title": "Batch of reports",
                      "required": [
                        "results",
                        "ranked_order",
                        "duplicate_clusters",
                        "request_id"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "results": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/defect_triage_triaged"
                          },
                          "description": "One record per submitted report, in submission order."
                        },
                        "ranked_order": {
                          "type": "array",
                          "items": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "description": "Indices into `results`, worst first. Sorted by severity, then by category risk (security, data, crash, performance, integration, ui, docs, other), then by submission order.",
                          "example": [
                            2,
                            0,
                            1
                          ]
                        },
                        "duplicate_clusters": {
                          "type": "array",
                          "description": "Groups of indices that look like the same bug, by token overlap. Only groups of two or more appear; an empty array means no suspected duplicates.",
                          "items": {
                            "type": "array",
                            "minItems": 2,
                            "items": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "description": "Indices into `results` that cluster together."
                          },
                          "example": [
                            [
                              0,
                              3
                            ]
                          ]
                        },
                        "request_id": {
                          "type": "string",
                          "description": "Identifier for this metered call."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/dependency-check": {
      "post": {
        "operationId": "dependency_check",
        "summary": "Dependency-manifest hygiene check",
        "description": "Dependency-manifest hygiene check. POST package manifests (package.json, requirements.txt, pyproject.toml, go.mod, Gemfile, composer.json, pubspec.yaml, lockfiles) or an upload_id and get findings with severity + concrete fixes: wildcard/unbounded versions, missing lockfiles, unpinned installs, non-https indexes, install-script risks, dependency-confusion signals, and matches against a static snapshot of famously compromised packages. NOT a live CVE feed - pair with npm audit / pip-audit / osv-scanner for that. Deterministic, no network, no LLM.\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "dependency_check",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "manifests": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "filename": {
                          "type": "string"
                        },
                        "content": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "filename",
                        "content"
                      ]
                    },
                    "description": "Up to 20 manifest files inline."
                  },
                  "upload_id": {
                    "type": "string",
                    "description": "Alternative: a single manifest uploaded via the uploads tool."
                  },
                  "filename": {
                    "type": "string",
                    "description": "Required with upload_id so the manifest type can be detected."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "findings",
                    "score_0_100",
                    "grade",
                    "summary",
                    "snapshot_as_of",
                    "note",
                    "request_id"
                  ],
                  "properties": {
                    "score_0_100": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 100,
                      "description": "Starts at 100 with a weighted deduction per finding. Findings are deduplicated before scoring, so one compromised package named in several places is counted once."
                    },
                    "grade": {
                      "type": "string",
                      "enum": [
                        "A",
                        "B",
                        "C",
                        "D",
                        "F"
                      ],
                      "description": "A at 90, B at 80, C at 70, D at 60, F below."
                    },
                    "findings": {
                      "type": "array",
                      "description": "Problems found, most severe first, then by file, category and message so ordering is stable across runs.",
                      "items": {
                        "type": "object",
                        "required": [
                          "file",
                          "severity",
                          "category",
                          "message",
                          "fix"
                        ],
                        "properties": {
                          "file": {
                            "type": "string",
                            "description": "Manifest the finding came from. The literal value \"(report)\" means the finding is about the report itself rather than about your code."
                          },
                          "severity": {
                            "type": "string",
                            "enum": [
                              "critical",
                              "high",
                              "medium",
                              "low"
                            ],
                            "description": "How urgent the finding is."
                          },
                          "category": {
                            "type": "string",
                            "enum": [
                              "compromised_package",
                              "dependency_confusion",
                              "install_scripts",
                              "insecure_source",
                              "unpinned_version",
                              "unpinned_url_dep",
                              "unpinned_git_dep",
                              "unbounded_range",
                              "wildcard_version",
                              "caret_zero",
                              "missing_lockfile",
                              "inconsistent_hashes",
                              "local_replace",
                              "missing_engines",
                              "missing_go_directive",
                              "missing_python_requires",
                              "missing_sdk_constraint",
                              "spec_not_evaluated",
                              "parse_error",
                              "unsupported_file",
                              "findings_truncated",
                              "analysis_budget_exceeded"
                            ],
                            "description": "Stable rule identifier. The last two are report-integrity categories, not code problems: they mean the report is INCOMPLETE - findings were dropped for size, or analysis stopped early, so later manifests may not be represented at all."
                          },
                          "message": {
                            "type": "string",
                            "maxLength": 1000,
                            "description": "What is wrong. Values interpolated from your manifest are clipped, since manifest content is untrusted."
                          },
                          "fix": {
                            "type": "string",
                            "maxLength": 1000,
                            "description": "What to do about it."
                          }
                        }
                      }
                    },
                    "summary": {
                      "type": "object",
                      "required": [
                        "by_severity",
                        "files_checked",
                        "manifest_types"
                      ],
                      "properties": {
                        "by_severity": {
                          "type": "object",
                          "description": "Finding counts per severity. All four keys are always present, including zeros.",
                          "required": [
                            "critical",
                            "high",
                            "medium",
                            "low"
                          ],
                          "additionalProperties": false,
                          "properties": {
                            "critical": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "Count of critical findings."
                            },
                            "high": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "Count of high findings."
                            },
                            "medium": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "Count of medium findings."
                            },
                            "low": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "Count of low findings."
                            }
                          }
                        },
                        "files_checked": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Recognised manifests actually analysed. Unsupported files are reported as a finding and excluded from this count."
                        },
                        "manifest_types": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Distinct manifest kinds recognised in the submission.",
                          "example": [
                            "package-lock.json",
                            "package.json"
                          ]
                        }
                      }
                    },
                    "snapshot_as_of": {
                      "type": "string",
                      "description": "Vintage of the compromised-package snapshot this scan matched against."
                    },
                    "note": {
                      "type": "string",
                      "description": "Standing caveat that this is a static snapshot, NOT a live CVE feed. Pair it with npm audit, pip-audit or osv-scanner for current vulnerability data."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/diagram-contrast": {
      "post": {
        "operationId": "diagram_contrast",
        "summary": "Diagram quality QA",
        "description": "Diagram quality QA. Catch and fix the things that ruin diagrams: (1) unreadable label contrast - POST a mermaid/SVG or HTML diagram, get every WCAG-AA failure (incl. mermaid <style>-block label colors, with unresolvable fills flagged honestly, not false-passed) + auto-corrected colors; (2) harden a mermaid SOURCE before it renders - rewrites every fill/label pair and remaps fills no label color can rescue, which scanning cannot do because a per-node style line overrides the theme block; (3) HTML-looks-fine-but-prints-broken - checks for the print-color-adjust/page-break gotchas and returns the fix CSS; (4) arrows crossing objects - returns the ELK-renderer config + layout rules that minimize crossings. Pure deterministic - no AI, no rate limit.\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "diagram_contrast",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "scan",
                      "harden",
                      "contrast",
                      "readable",
                      "palette",
                      "print",
                      "layout"
                    ],
                    "description": "Default \"scan\". scan=check rendered output; harden=fix a mermaid source before rendering; print=print-safety check; layout=arrow-crossing reduction."
                  },
                  "content": {
                    "type": "string",
                    "description": "scan/print: the SVG or HTML diagram/document source."
                  },
                  "source": {
                    "type": "string",
                    "description": "harden: the mermaid SOURCE text (not rendered SVG)."
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "svg",
                      "html"
                    ],
                    "description": "scan: content format (default svg)."
                  },
                  "autofix": {
                    "type": "boolean",
                    "description": "scan: include suggested corrected colors."
                  },
                  "add_init": {
                    "type": "boolean",
                    "description": "harden: prepend the %%{init}%% theme block (default true)."
                  },
                  "fg": {
                    "type": "string",
                    "description": "contrast: foreground hex (#rrggbb)."
                  },
                  "bg": {
                    "type": "string",
                    "description": "contrast/readable: background/fill hex (#rrggbb)."
                  },
                  "large": {
                    "type": "boolean",
                    "description": "contrast/harden: use the 3:1 large-text threshold."
                  },
                  "brand": {
                    "type": "string",
                    "description": "brand palette to use (e.g. appstango). Default from config."
                  },
                  "renderer": {
                    "type": "string",
                    "description": "layout: mermaid renderer (default \"elk\")."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "description": "Seven modes, seven shapes. `mode` is echoed on every response and is the discriminator - switch on it before reading anything else.",
                  "oneOf": [
                    {
                      "type": "object",
                      "title": "scan - audit a rendered diagram",
                      "required": [
                        "mode",
                        "brand",
                        "format",
                        "checked",
                        "pass",
                        "failure_count",
                        "unverified_count",
                        "failures",
                        "unverified",
                        "note",
                        "request_id"
                      ],
                      "properties": {
                        "mode": {
                          "type": "string",
                          "const": "scan",
                          "description": "Discriminator."
                        },
                        "brand": {
                          "type": "string",
                          "description": "Palette used to judge and to propose fixes."
                        },
                        "format": {
                          "type": "string",
                          "enum": [
                            "svg",
                            "html"
                          ],
                          "description": "How the content was parsed."
                        },
                        "checked": {
                          "type": "boolean",
                          "description": "Always true; present so a caller can distinguish a real scan from a skipped one."
                        },
                        "pass": {
                          "type": "boolean",
                          "description": "True ONLY when there are zero failures AND zero unverified nodes. An unresolvable background never counts as passing - see unverified."
                        },
                        "failure_count": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Number of genuine AA failures."
                        },
                        "unverified_count": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Number of nodes whose background could not be resolved statically."
                        },
                        "failures": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/diagram_contrast_failure"
                          },
                          "description": "Real contrast failures, with both colours known."
                        },
                        "unverified": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/diagram_contrast_unverifiedFailure"
                          },
                          "description": "Nodes whose fill is a gradient, url(), var() or currentColor. Reported honestly rather than silently passed."
                        },
                        "fixes": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/diagram_contrast_fix"
                          },
                          "description": "Present only when autofix was requested. Covers the resolvable failures only."
                        },
                        "note": {
                          "type": "string",
                          "description": "Plain-English summary, including an explicit warning when unverified nodes exist."
                        },
                        "request_id": {
                          "type": "string",
                          "description": "Identifier for this metered call."
                        }
                      }
                    },
                    {
                      "type": "object",
                      "title": "harden - fix a mermaid source before it renders",
                      "required": [
                        "mode",
                        "brand",
                        "source",
                        "changed",
                        "threshold",
                        "pass",
                        "style_lines_hardened",
                        "fills_remapped",
                        "changes",
                        "note",
                        "unresolved",
                        "request_id"
                      ],
                      "properties": {
                        "mode": {
                          "type": "string",
                          "const": "harden",
                          "description": "Discriminator."
                        },
                        "brand": {
                          "type": "string",
                          "description": "Palette the rewrite drew from."
                        },
                        "source": {
                          "type": "string",
                          "description": "The hardened mermaid source, ready to render. Idempotent - hardening an already-hardened source is a no-op."
                        },
                        "changed": {
                          "type": "boolean",
                          "description": "False when the source was already safe and nothing was rewritten."
                        },
                        "threshold": {
                          "type": "number",
                          "description": "Contrast ratio every rewritten pair had to clear: 3.0 with large, otherwise 4.5.",
                          "example": 4.5
                        },
                        "pass": {
                          "type": "boolean",
                          "description": "True when every rewritten pair meets the threshold. When false, `unresolved` says which did not - do NOT treat the source as safe."
                        },
                        "style_lines_hardened": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "How many style/classDef lines were rewritten."
                        },
                        "fills_remapped": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "How many fills had to be replaced outright because no label colour could rescue them."
                        },
                        "changes": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/diagram_contrast_hardenChange"
                          },
                          "description": "Every rewrite applied."
                        },
                        "unresolved": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/diagram_contrast_hardenChange"
                          },
                          "description": "The subset of changes that still fall short of the threshold. Empty when pass is true."
                        },
                        "note": {
                          "type": "string",
                          "description": "Explicit statement of whether the output can be trusted."
                        },
                        "request_id": {
                          "type": "string",
                          "description": "Identifier for this metered call."
                        }
                      }
                    },
                    {
                      "type": "object",
                      "title": "contrast - check one colour pair",
                      "required": [
                        "mode",
                        "fg",
                        "bg",
                        "ratio",
                        "threshold",
                        "passes",
                        "request_id"
                      ],
                      "properties": {
                        "mode": {
                          "type": "string",
                          "const": "contrast",
                          "description": "Discriminator."
                        },
                        "fg": {
                          "type": "string",
                          "description": "Foreground colour as submitted.",
                          "example": "#767676"
                        },
                        "bg": {
                          "type": "string",
                          "description": "Background colour as submitted.",
                          "example": "#ffffff"
                        },
                        "ratio": {
                          "type": "number",
                          "minimum": 1,
                          "maximum": 21,
                          "description": "WCAG contrast ratio, 1 to 21.",
                          "example": 4.54
                        },
                        "threshold": {
                          "type": "number",
                          "description": "3.0 when large is set, otherwise 4.5.",
                          "example": 4.5
                        },
                        "passes": {
                          "type": "boolean",
                          "description": "Whether ratio meets the threshold."
                        },
                        "request_id": {
                          "type": "string",
                          "description": "Identifier for this metered call."
                        }
                      }
                    },
                    {
                      "type": "object",
                      "title": "readable - pick a readable text colour for a background",
                      "required": [
                        "mode",
                        "brand",
                        "bg",
                        "text_color",
                        "ratio",
                        "request_id"
                      ],
                      "properties": {
                        "mode": {
                          "type": "string",
                          "const": "readable",
                          "description": "Discriminator."
                        },
                        "brand": {
                          "type": "string",
                          "description": "Palette the ink and paper colours came from."
                        },
                        "bg": {
                          "type": "string",
                          "description": "The background you supplied."
                        },
                        "text_color": {
                          "type": "string",
                          "description": "Whichever of the palette ink or paper reads better on that background.",
                          "example": "#1a1a1a"
                        },
                        "ratio": {
                          "type": "number",
                          "minimum": 1,
                          "maximum": 21,
                          "description": "Contrast ratio achieved by that choice."
                        },
                        "request_id": {
                          "type": "string",
                          "description": "Identifier for this metered call."
                        }
                      }
                    },
                    {
                      "type": "object",
                      "title": "palette - inspect a brand palette",
                      "required": [
                        "mode",
                        "brand",
                        "provisional",
                        "palette",
                        "request_id"
                      ],
                      "properties": {
                        "mode": {
                          "type": "string",
                          "const": "palette",
                          "description": "Discriminator."
                        },
                        "brand": {
                          "type": "string",
                          "description": "Resolved palette name."
                        },
                        "provisional": {
                          "type": "boolean",
                          "description": "True when the palette is a placeholder not yet signed off by the brand owner."
                        },
                        "palette": {
                          "type": "object",
                          "description": "Semantic colour roles. Keys vary by brand; internal keys prefixed with an underscore are stripped. Values set brand only - readability is computed per fill regardless.",
                          "additionalProperties": {
                            "type": "string",
                            "description": "A colour value, usually a hex string."
                          },
                          "properties": {
                            "paper": {
                              "type": "string",
                              "description": "Light surface colour."
                            },
                            "ink": {
                              "type": "string",
                              "description": "Dark text and heading colour."
                            },
                            "accent": {
                              "type": "string",
                              "description": "Primary accent."
                            },
                            "muted": {
                              "type": "string",
                              "description": "Secondary text and lines."
                            },
                            "line": {
                              "type": "string",
                              "description": "Borders."
                            }
                          }
                        },
                        "request_id": {
                          "type": "string",
                          "description": "Identifier for this metered call."
                        }
                      }
                    },
                    {
                      "type": "object",
                      "title": "print - check an HTML document survives printing",
                      "required": [
                        "mode",
                        "print_ready",
                        "issues",
                        "print_css",
                        "request_id"
                      ],
                      "properties": {
                        "mode": {
                          "type": "string",
                          "const": "print",
                          "description": "Discriminator."
                        },
                        "print_ready": {
                          "type": "boolean",
                          "description": "True when no issues were found. Also true when the document has no coloured diagram at all, in which case `note` says print safety was not applicable."
                        },
                        "issues": {
                          "type": "array",
                          "description": "Print-breakage causes found. Empty when print_ready is true.",
                          "items": {
                            "type": "object",
                            "required": [
                              "check",
                              "issue"
                            ],
                            "properties": {
                              "check": {
                                "type": "string",
                                "enum": [
                                  "print_color_adjust",
                                  "media_print",
                                  "break_inside"
                                ],
                                "description": "Which check failed: missing print-color-adjust (fills print white), missing @media print block, or missing break-inside (a page break can slice a diagram)."
                              },
                              "issue": {
                                "type": "string",
                                "description": "What goes wrong and why."
                              }
                            }
                          }
                        },
                        "fix": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "One-line remediation, or null when there is nothing to fix."
                        },
                        "note": {
                          "type": "string",
                          "description": "Present only when no coloured diagram was detected, explaining that the check did not apply."
                        },
                        "print_css": {
                          "type": "string",
                          "description": "A ready-to-paste @media print block that resolves all three checks."
                        },
                        "request_id": {
                          "type": "string",
                          "description": "Identifier for this metered call."
                        }
                      }
                    },
                    {
                      "type": "object",
                      "title": "layout - reduce arrow crossings",
                      "required": [
                        "mode",
                        "init_fragment",
                        "rules",
                        "note",
                        "request_id"
                      ],
                      "properties": {
                        "mode": {
                          "type": "string",
                          "const": "layout",
                          "description": "Discriminator."
                        },
                        "init_fragment": {
                          "type": "object",
                          "description": "Merge into your mermaid init block. Empty object for any renderer other than elk, since only elk needs the directive.",
                          "additionalProperties": true,
                          "properties": {
                            "layout": {
                              "type": "string",
                              "description": "Renderer to use.",
                              "example": "elk"
                            }
                          }
                        },
                        "rules": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Authoring rules that reduce crossings, most effective first."
                        },
                        "note": {
                          "type": "string",
                          "description": "Standing caveat that crossings can be reduced but not guaranteed to zero."
                        },
                        "request_id": {
                          "type": "string",
                          "description": "Identifier for this metered call."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/humanize": {
      "post": {
        "operationId": "humanize",
        "summary": "Rewrite text to remove AI-writing tells",
        "description": "Rewrite text to remove AI-writing tells. POST prose and get it back with the mechanical tells fixed (AI vocabulary swapped for plain words, filler phrases stripped, em-dash overuse reduced, significance inflation deflated) plus a change log, before/after ai-ness scores, and a residual list of tells that need human judgment (not auto-rewritten). Code fences, inline code, URLs, and quotes are never touched. Deterministic, no LLM. Sister to humanizer_score (the detector).\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "humanize",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "The prose to rewrite (up to 100k chars)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "rewritten",
                    "changes",
                    "score_before",
                    "score_after",
                    "residual",
                    "request_id"
                  ],
                  "properties": {
                    "rewritten": {
                      "type": "string",
                      "description": "The rewritten prose. Code fences, inline code, URLs and quoted passages are returned untouched."
                    },
                    "changes": {
                      "type": "array",
                      "description": "Every substitution applied, in document order. Empty when nothing mechanical was found to fix.",
                      "items": {
                        "type": "object",
                        "required": [
                          "pattern",
                          "before",
                          "after",
                          "position"
                        ],
                        "properties": {
                          "pattern": {
                            "type": "string",
                            "enum": [
                              "ai_vocabulary",
                              "significance_inflation",
                              "promotional_language",
                              "filler_phrases",
                              "negative_parallelism",
                              "rule_of_three",
                              "em_dash_overuse"
                            ],
                            "description": "Which rule triggered this edit."
                          },
                          "before": {
                            "type": "string",
                            "description": "The original text that was replaced.",
                            "example": "delve into"
                          },
                          "after": {
                            "type": "string",
                            "description": "What replaced it. An empty string means the phrase was deleted outright.",
                            "example": "examine"
                          },
                          "position": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Character offset in the ORIGINAL text, not the rewritten one."
                          }
                        }
                      }
                    },
                    "score_before": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "minimum": 0,
                      "maximum": 1,
                      "description": "ai_ness of the submitted text, on the same scale humanizer_score reports. Null if the scorer was unavailable."
                    },
                    "score_after": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "minimum": 0,
                      "maximum": 1,
                      "description": "ai_ness of the rewritten text. Compare with score_before to show the improvement. Null if the scorer was unavailable."
                    },
                    "residual": {
                      "type": "array",
                      "description": "Tells that were deliberately NOT auto-rewritten because fixing them needs human judgement. Sorted by position.",
                      "items": {
                        "type": "object",
                        "required": [
                          "pattern",
                          "match",
                          "position",
                          "advice"
                        ],
                        "properties": {
                          "pattern": {
                            "type": "string",
                            "enum": [
                              "negative_parallelism",
                              "rule_of_three",
                              "promotional_language"
                            ],
                            "description": "Which tell remains. Only these three are ever left for a human."
                          },
                          "match": {
                            "type": "string",
                            "maxLength": 60,
                            "description": "The offending text, truncated to 60 characters."
                          },
                          "position": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Character offset in the original text."
                          },
                          "advice": {
                            "type": "string",
                            "description": "What a human should do about it."
                          }
                        }
                      }
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/humanizer-score": {
      "post": {
        "operationId": "humanizer_score",
        "summary": "Detect AI-writing tells in text",
        "description": "Detect AI-writing tells in text. POST prose and get a 0-1 ai_ness score + band (low/medium/high) + the EXACT patterns that fired (AI vocabulary, significance inflation, promotional language, filler phrases, negative parallelism, rule-of-three, em-dash overuse) with sample spans. Explainable, not an opaque classifier - every point is a named, located pattern. Deterministic, no LLM. The rewrite is a separate step.\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "humanizer_score",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "The prose to score (up to 100k chars)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ai_ness",
                    "band",
                    "words",
                    "total_flags",
                    "breakdown",
                    "sample_spans",
                    "recommendation",
                    "request_id"
                  ],
                  "properties": {
                    "ai_ness": {
                      "type": "number",
                      "minimum": 0,
                      "maximum": 1,
                      "example": 0.42,
                      "description": "Overall AI-writing score. 0 reads fully human, 1 is saturated with tells."
                    },
                    "band": {
                      "type": "string",
                      "enum": [
                        "low",
                        "medium",
                        "high"
                      ],
                      "description": "Bucketed score: low below 0.15, medium below 0.40, high at or above it."
                    },
                    "words": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Word count of the submitted text, the denominator for every density-based pattern."
                    },
                    "total_flags": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Total number of pattern hits found across all categories."
                    },
                    "breakdown": {
                      "type": "array",
                      "description": "Per-pattern contribution, highest contribution first. Patterns that did not fire are omitted.",
                      "items": {
                        "type": "object",
                        "required": [
                          "pattern",
                          "count",
                          "contribution"
                        ],
                        "properties": {
                          "pattern": {
                            "type": "string",
                            "enum": [
                              "ai_vocabulary",
                              "significance_inflation",
                              "promotional_language",
                              "filler_phrases",
                              "negative_parallelism",
                              "rule_of_three",
                              "em_dash_overuse"
                            ],
                            "description": "Which detector fired."
                          },
                          "count": {
                            "type": "integer",
                            "minimum": 1,
                            "description": "How many times this pattern matched."
                          },
                          "contribution": {
                            "type": "number",
                            "description": "How much this pattern added to ai_ness, rounded to 3 decimals."
                          }
                        }
                      }
                    },
                    "sample_spans": {
                      "type": "array",
                      "maxItems": 8,
                      "description": "Example matches so a caller can show the user what fired and where. Up to 3 per pattern, 8 overall.",
                      "items": {
                        "type": "object",
                        "required": [
                          "pattern",
                          "match",
                          "pos"
                        ],
                        "properties": {
                          "pattern": {
                            "type": "string",
                            "enum": [
                              "ai_vocabulary",
                              "significance_inflation",
                              "promotional_language",
                              "filler_phrases",
                              "negative_parallelism",
                              "rule_of_three",
                              "em_dash_overuse"
                            ],
                            "description": "Which detector produced this span."
                          },
                          "match": {
                            "type": "string",
                            "description": "The matched text.",
                            "example": "delve into"
                          },
                          "pos": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Character offset of the match in the submitted text."
                          }
                        }
                      }
                    },
                    "recommendation": {
                      "type": "string",
                      "enum": [
                        "reads human",
                        "light edit - a few AI tells",
                        "rewrite recommended - strong AI tells"
                      ],
                      "description": "Plain-English verdict derived from the band."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/log-analyzer": {
      "post": {
        "operationId": "log_analyzer",
        "summary": "Analyze a log file",
        "description": "Analyze a log file. POST raw log text (or an upload_id from the uploads tool, up to 5MB) and get: level breakdown + time range, error clusters (messages normalized so variable ids/numbers collapse into one signature, with counts + first/last seen + a sample), a timeline, detected anomalies (error spikes, retry storms, new error signatures, log gaps/outages, level-ratio shifts) with evidence, and templated recommendations. Handles JSON-lines and plain formats (ISO8601/syslog/CLF/epoch timestamps). Deterministic, single-pass, no LLM.\n\nCosts **2 credits** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "log_analyzer",
        "x-credit-cost": 2,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "Raw log content (up to 1M chars)."
                  },
                  "upload_id": {
                    "type": "string",
                    "description": "Alternative: a log file uploaded via the uploads tool."
                  },
                  "top_n": {
                    "type": "number",
                    "description": "Top clusters to return (1-50, default 10)."
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "auto",
                      "json",
                      "plain"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "summary",
                    "error_clusters",
                    "top_errors",
                    "anomalies",
                    "timeline",
                    "recommendations",
                    "request_id"
                  ],
                  "properties": {
                    "summary": {
                      "type": "object",
                      "required": [
                        "total_lines",
                        "parsed_lines",
                        "parse_rate",
                        "time_range",
                        "levels"
                      ],
                      "properties": {
                        "total_lines": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Lines received."
                        },
                        "parsed_lines": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Lines from which a timestamp or level could be extracted."
                        },
                        "parse_rate": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1,
                          "description": "parsed_lines over total_lines. Below 0.5 the time-based findings are weak, and a recommendation says so."
                        },
                        "time_range": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "description": "Span covered by the log. Null when no line carried a parseable timestamp, in which case the timeline is empty too.",
                          "required": [
                            "start",
                            "end",
                            "duration_s"
                          ],
                          "properties": {
                            "start": {
                              "type": "string",
                              "format": "date-time",
                              "description": "Earliest timestamp seen."
                            },
                            "end": {
                              "type": "string",
                              "format": "date-time",
                              "description": "Latest timestamp seen."
                            },
                            "duration_s": {
                              "type": "number",
                              "description": "Seconds between them."
                            }
                          }
                        },
                        "levels": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          },
                          "description": "Line count per log level, most frequent first.",
                          "example": {
                            "INFO": 1400,
                            "ERROR": 62,
                            "WARN": 31
                          }
                        },
                        "truncated": {
                          "type": "boolean",
                          "const": true,
                          "description": "Present only when the input exceeded the size limit and was cut short - the report does not cover the whole log."
                        },
                        "timeline_note": {
                          "type": "string",
                          "description": "Present only when the timeline could not be built, explaining why."
                        },
                        "timeline_bucket_s": {
                          "type": "integer",
                          "description": "Bucket width in seconds, chosen automatically from the duration. Absent when there is no timeline."
                        }
                      }
                    },
                    "error_clusters": {
                      "type": "array",
                      "description": "Distinct error signatures, most frequent first. Variable ids, numbers and paths are normalised out, so one recurring error collapses into a single entry rather than thousands.",
                      "items": {
                        "$ref": "#/components/schemas/log_analyzer_cluster"
                      }
                    },
                    "top_errors": {
                      "type": "array",
                      "description": "The first top_n entries of error_clusters. A convenience view - nothing here is absent from error_clusters.",
                      "items": {
                        "$ref": "#/components/schemas/log_analyzer_cluster"
                      }
                    },
                    "anomalies": {
                      "type": "array",
                      "description": "Detected irregularities, each with the evidence that triggered it. Empty when the log looks steady.",
                      "items": {
                        "type": "object",
                        "required": [
                          "type",
                          "detail",
                          "evidence"
                        ],
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "error_rate_spike",
                              "new_error_signature",
                              "retry_storm",
                              "log_gap",
                              "level_ratio_shift",
                              "stack_traces"
                            ],
                            "description": "What kind of anomaly: an error burst, an error signature appearing only late in the window, repeated retries, a silent period suggesting an outage, a shift in the level mix, or unhandled exceptions."
                          },
                          "detail": {
                            "type": "string",
                            "description": "The rule that fired, in plain English."
                          },
                          "evidence": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "The numbers behind the call. Keys vary by anomaly type - for example a spike carries bucket_start, errors and median_nonzero."
                          }
                        }
                      }
                    },
                    "timeline": {
                      "type": "array",
                      "description": "Level counts per time bucket, oldest first. Empty when no timestamps were found.",
                      "items": {
                        "type": "object",
                        "required": [
                          "bucket_start",
                          "counts_by_level"
                        ],
                        "properties": {
                          "bucket_start": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Start of the bucket."
                          },
                          "counts_by_level": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "integer"
                            },
                            "description": "Lines per level in this bucket. Levels absent from the bucket are omitted rather than zero."
                          }
                        }
                      }
                    },
                    "recommendations": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Templated next steps derived from the findings. Never empty - a clean log yields an explicit \"no anomalies detected\" line."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/log-audit-creator": {
      "post": {
        "operationId": "log_audit_creator",
        "summary": "Generate a concrete audit-logging spec for an app",
        "description": "Generate a concrete audit-logging spec for an app. POST your feature list (and optional compliance targets: soc2/hipaa/gdpr/pci) and get a full audit-event catalog (namespaced event names, actor/target/required fields, PII flags, retention), a canonical event envelope schema, a storage plan (append-only, access control, alerting), and per-framework compliance notes. A dev team can implement it directly. Deterministic, no LLM. Retention/compliance figures are common-practice defaults to verify with counsel.\n\nCosts **2 credits** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "log_audit_creator",
        "x-credit-cost": 2,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "features": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "App features, e.g. [\"user login\", \"payments\", \"file upload\"]."
                  },
                  "compliance": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "soc2",
                        "hipaa",
                        "gdpr",
                        "pci"
                      ]
                    }
                  },
                  "description": {
                    "type": "string",
                    "description": "Optional free-text app description (scanned for features)."
                  },
                  "stack": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "note",
                    "events",
                    "schema",
                    "storage_plan",
                    "coverage_notes",
                    "compliance_notes",
                    "request_id"
                  ],
                  "properties": {
                    "note": {
                      "type": "string",
                      "description": "Standing caveat that retention and compliance figures are common-practice defaults, not legal advice - verify with counsel before relying on them."
                    },
                    "events": {
                      "type": "array",
                      "description": "The audit-event catalog, sorted by event name. A developer can implement this directly.",
                      "items": {
                        "type": "object",
                        "required": [
                          "event_name",
                          "trigger",
                          "actor_fields",
                          "target_fields",
                          "required_fields",
                          "pii_fields",
                          "retention_days",
                          "severity"
                        ],
                        "properties": {
                          "event_name": {
                            "type": "string",
                            "description": "Dot-namespaced event name.",
                            "example": "auth.login.failure"
                          },
                          "trigger": {
                            "type": "string",
                            "description": "What in the application should emit this event."
                          },
                          "actor_fields": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Fields identifying who did it.",
                            "example": [
                              "actor_id",
                              "actor_type",
                              "ip",
                              "user_agent"
                            ]
                          },
                          "target_fields": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Fields identifying what was acted on. Empty for events with no distinct target."
                          },
                          "required_fields": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Fields every event must carry regardless of type.",
                            "example": [
                              "event_id",
                              "event_name",
                              "timestamp",
                              "request_id",
                              "outcome"
                            ]
                          },
                          "pii_fields": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Which fields on this event carry personal data and therefore need masking, encryption or a shorter retention. Empty when none do."
                          },
                          "retention_days": {
                            "type": "integer",
                            "minimum": 1,
                            "description": "How long to keep this event. Defaults to 365 and is raised to the highest applicable compliance floor when a framework is requested.",
                            "example": 365
                          },
                          "severity": {
                            "type": "string",
                            "enum": [
                              "info",
                              "warning",
                              "critical"
                            ],
                            "description": "How much attention the event warrants. Critical events are the alerting candidates."
                          }
                        }
                      }
                    },
                    "schema": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "A JSON Schema for the canonical event envelope every event shares. This is a schema document, not data - hand it to your validator."
                    },
                    "storage_plan": {
                      "type": "object",
                      "description": "How to store the log so it is trustworthy. Each list is a set of concrete requirements rather than prose.",
                      "required": [
                        "retention",
                        "immutability",
                        "separation",
                        "access_control",
                        "alerting"
                      ],
                      "properties": {
                        "retention": {
                          "type": "object",
                          "required": [
                            "default_days",
                            "note"
                          ],
                          "properties": {
                            "default_days": {
                              "type": "integer",
                              "description": "Baseline retention. Individual events may exceed it; the highest applicable floor wins."
                            },
                            "note": {
                              "type": "string",
                              "description": "Explanation of how per-event retention interacts with the default."
                            }
                          }
                        },
                        "immutability": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Append-only, WORM storage and tamper-detection requirements."
                        },
                        "separation": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Why and how to keep the audit trail out of the application database."
                        },
                        "access_control": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Who may write and who may read, including auditing the auditors."
                        },
                        "alerting": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Which events should page someone."
                        },
                        "stack_hint": {
                          "type": "string",
                          "description": "Present only when you supplied a stack: how to emit the envelope idiomatically there."
                        }
                      }
                    },
                    "coverage_notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Warnings about the generated catalog itself - notably when no feature keyword matched and only baseline events were produced, which means coverage is thinner than it looks."
                    },
                    "compliance_notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Framework-specific obligations added by the requested compliance targets. Empty when none were requested."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/model-advisor": {
      "post": {
        "operationId": "model_advisor",
        "summary": "LLM selection + cost advisor",
        "description": "LLM selection + cost advisor. POST a task description (+ optional volume/tokens/constraints like local-only or cheap) and get a recommended model + fallback chain + do-not-use (with reasons), the complexity tier, and per-run + monthly cost. A maintained model+price catalog. Deterministic, no LLM. Tells the AI/user which model to use and what it costs.\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "model_advisor",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "task"
                ],
                "properties": {
                  "task": {
                    "type": "string"
                  },
                  "volume_per_month": {
                    "type": "number"
                  },
                  "tokens_in": {
                    "type": "number"
                  },
                  "tokens_out": {
                    "type": "number"
                  },
                  "constraints": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "complexity_tier",
                    "selection_tier",
                    "recommendation",
                    "cost_estimate",
                    "catalog_as_of",
                    "rationale",
                    "request_id"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "ok",
                      "description": "Always \"ok\" on a 200. Failure to find any eligible model is a 400, not a status value."
                    },
                    "complexity_tier": {
                      "type": "string",
                      "enum": [
                        "S0",
                        "S1",
                        "S2",
                        "S3",
                        "S4"
                      ],
                      "description": "How hard the task is, classified from keyword signals. S0 is trivial, S4 is architectural or high-stakes judgement. Defaults to S2 when nothing matches."
                    },
                    "selection_tier": {
                      "type": "string",
                      "enum": [
                        "S0",
                        "S1",
                        "S2",
                        "S3",
                        "S4"
                      ],
                      "description": "The tier actually shopped for. Equals complexity_tier unless the \"cheap\" constraint shifted selection down one tier, in which case expect a quality trade-off."
                    },
                    "tier_fit_warning": {
                      "type": "string",
                      "description": "Present only when the selected model is an imperfect fit for the tier. Absent means no caveat."
                    },
                    "recommendation": {
                      "type": "object",
                      "required": [
                        "primary",
                        "fallbacks",
                        "also_considered",
                        "do_not_use"
                      ],
                      "properties": {
                        "primary": {
                          "type": "object",
                          "description": "The cheapest model covering the selection tier. Local models win ties, since their marginal cost is zero.",
                          "required": [
                            "model",
                            "provider",
                            "local",
                            "as_of",
                            "source",
                            "reason"
                          ],
                          "properties": {
                            "model": {
                              "type": "string",
                              "description": "Model identifier.",
                              "example": "claude-haiku-4-5-20251001"
                            },
                            "provider": {
                              "type": "string",
                              "description": "Who serves it.",
                              "example": "anthropic"
                            },
                            "local": {
                              "type": "boolean",
                              "description": "True for a model you can run yourself, where marginal cost is zero."
                            },
                            "as_of": {
                              "type": "string",
                              "description": "Date this model entry was last priced.",
                              "example": "2026-07-01"
                            },
                            "source": {
                              "type": "string",
                              "description": "Where the pricing came from."
                            },
                            "reason": {
                              "type": "string",
                              "description": "Why this model, in plain English."
                            }
                          }
                        },
                        "fallbacks": {
                          "type": "array",
                          "maxItems": 2,
                          "items": {
                            "$ref": "#/components/schemas/model_advisor_alternative"
                          },
                          "description": "Up to two next-cheapest models covering the same tier. Use these when the primary is unavailable."
                        },
                        "also_considered": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/model_advisor_alternative"
                          },
                          "description": "Eligible but ranked lower by cost. Shown so the ranking is auditable rather than opaque."
                        },
                        "do_not_use": {
                          "type": "array",
                          "description": "Models explicitly ruled out, each with the reason - underpowered, overkill, or excluded by a constraint.",
                          "items": {
                            "type": "object",
                            "required": [
                              "model",
                              "reason"
                            ],
                            "properties": {
                              "model": {
                                "type": "string",
                                "description": "Model identifier."
                              },
                              "reason": {
                                "type": "string",
                                "description": "Why it was rejected."
                              }
                            }
                          }
                        }
                      }
                    },
                    "cost_estimate": {
                      "type": "object",
                      "required": [
                        "price_per_run_usd",
                        "monthly_cost",
                        "assumptions"
                      ],
                      "properties": {
                        "price_per_run_usd": {
                          "type": "number",
                          "description": "Estimated USD for one run of the primary model at the assumed token counts.",
                          "example": 0.0021
                        },
                        "monthly_cost": {
                          "type": "number",
                          "description": "price_per_run_usd multiplied by the monthly volume, rounded to 4 decimals.",
                          "example": 21
                        },
                        "assumptions": {
                          "type": "object",
                          "description": "Every input the estimate depends on, echoed so the number can be recomputed or challenged.",
                          "required": [
                            "tokens_in",
                            "tokens_out",
                            "volume_per_month",
                            "pricing_as_of",
                            "pricing_source"
                          ],
                          "properties": {
                            "tokens_in": {
                              "type": "integer",
                              "description": "Input tokens assumed per run. Uses a default when you did not supply one."
                            },
                            "tokens_out": {
                              "type": "integer",
                              "description": "Output tokens assumed per run."
                            },
                            "volume_per_month": {
                              "type": "integer",
                              "description": "Runs per month assumed."
                            },
                            "pricing_as_of": {
                              "type": "string",
                              "description": "Date the primary model was priced."
                            },
                            "pricing_source": {
                              "type": "string",
                              "description": "Where that price came from."
                            }
                          }
                        }
                      }
                    },
                    "catalog_as_of": {
                      "type": "string",
                      "description": "Date of the catalog as a whole. This is a maintained snapshot of public list prices reviewed monthly, NOT a live pricing feed - verify before committing spend."
                    },
                    "rationale": {
                      "type": "string",
                      "description": "Narrative explanation of the classification, the ranking, and the pricing vintage."
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/saas-metrics": {
      "post": {
        "operationId": "saas_metrics",
        "summary": "SaaS health advisor",
        "description": "SaaS health advisor. POST your MRR/customers (+ optional churn/CAC/margin/growth) and get ARR/ARPA/LTV/CAC/payback/Rule-of-40/NRR/GRR computed, each with a health tag, plus an overall grade + a ranked \"what to fix first\" focus list. Deterministic math + advisory, no LLM.\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "saas_metrics",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "mrr",
                  "customers"
                ],
                "properties": {
                  "mrr": {
                    "type": "number"
                  },
                  "customers": {
                    "type": "number"
                  },
                  "prior_mrr": {
                    "type": "number"
                  },
                  "new_mrr": {
                    "type": "number"
                  },
                  "churned_mrr": {
                    "type": "number"
                  },
                  "expansion_mrr": {
                    "type": "number"
                  },
                  "cac": {
                    "type": "number"
                  },
                  "gross_margin_pct": {
                    "type": "number"
                  },
                  "sales_marketing_spend": {
                    "type": "number"
                  },
                  "new_customers": {
                    "type": "number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "metrics",
                    "health",
                    "advisory",
                    "request_id"
                  ],
                  "properties": {
                    "metrics": {
                      "type": "object",
                      "description": "Every metric derivable from the inputs supplied. A metric whose inputs were absent is present but null - the key never disappears, so a caller can distinguish \"not computed\" from \"not a field\".",
                      "required": [
                        "mrr",
                        "arr",
                        "arpa",
                        "gross_margin_pct",
                        "mrr_growth_pct",
                        "cac",
                        "ltv",
                        "ltv_cac_ratio",
                        "payback_months",
                        "rule_of_40",
                        "nrr_pct",
                        "grr_pct"
                      ],
                      "properties": {
                        "mrr": {
                          "type": "number",
                          "description": "Monthly recurring revenue, echoed back rounded to 2 decimals."
                        },
                        "arr": {
                          "type": "number",
                          "description": "Annual recurring revenue (MRR x 12)."
                        },
                        "arpa": {
                          "type": "number",
                          "description": "Average revenue per account (MRR / customers)."
                        },
                        "gross_margin_pct": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "description": "Gross margin as a percentage. Taken from gross_margin_pct if given, else derived from revenue and cogs."
                        },
                        "mrr_growth_pct": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "description": "Month-over-month MRR growth percentage. Derived from prior_mrr when present, else the supplied growth_pct."
                        },
                        "cac": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "description": "Customer acquisition cost. Explicit cac wins, else sales_marketing_spend / new_customers."
                        },
                        "ltv": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "description": "Lifetime value. Needs ARPA, gross margin and a non-zero churn rate; null otherwise."
                        },
                        "ltv_cac_ratio": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "description": "LTV divided by CAC. 3.0 is the usual healthy floor."
                        },
                        "payback_months": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "description": "Months for gross profit per account to repay CAC. Null when the result is unbounded."
                        },
                        "rule_of_40": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "description": "Growth percentage plus margin percentage. 40 is the benchmark."
                        },
                        "nrr_pct": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "description": "Net revenue retention percentage, including expansion. Needs prior_mrr plus at least one movement component."
                        },
                        "grr_pct": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "description": "Gross revenue retention percentage, excluding expansion."
                        }
                      }
                    },
                    "health": {
                      "type": "object",
                      "description": "A benchmark judgement for each of the six metrics that carry one. Always present for all six, using status \"unknown\" when the metric could not be computed.",
                      "required": [
                        "ltv_cac_ratio",
                        "payback_months",
                        "rule_of_40",
                        "nrr_pct",
                        "gross_margin_pct",
                        "mrr_growth_pct"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "ltv_cac_ratio": {
                          "$ref": "#/components/schemas/saas_metrics_healthTag"
                        },
                        "payback_months": {
                          "$ref": "#/components/schemas/saas_metrics_healthTag"
                        },
                        "rule_of_40": {
                          "$ref": "#/components/schemas/saas_metrics_healthTag"
                        },
                        "nrr_pct": {
                          "$ref": "#/components/schemas/saas_metrics_healthTag"
                        },
                        "gross_margin_pct": {
                          "$ref": "#/components/schemas/saas_metrics_healthTag"
                        },
                        "mrr_growth_pct": {
                          "$ref": "#/components/schemas/saas_metrics_healthTag"
                        }
                      }
                    },
                    "advisory": {
                      "type": "object",
                      "description": "The overall read, rolled up from the individual health tags.",
                      "required": [
                        "grade",
                        "headline",
                        "focus",
                        "strengths"
                      ],
                      "properties": {
                        "grade": {
                          "type": "string",
                          "enum": [
                            "healthy",
                            "watch",
                            "at_risk",
                            "insufficient_data"
                          ],
                          "description": "Worst-case roll-up. \"at_risk\" if any metric is unhealthy or failing, \"watch\" if any is borderline or concerning, \"insufficient_data\" when nothing could be computed."
                        },
                        "headline": {
                          "type": "string",
                          "description": "One-sentence summary of the grade."
                        },
                        "focus": {
                          "type": "array",
                          "description": "What to fix, worst first. Empty when nothing is below benchmark.",
                          "items": {
                            "type": "object",
                            "required": [
                              "metric",
                              "action"
                            ],
                            "properties": {
                              "metric": {
                                "type": "string",
                                "enum": [
                                  "ltv_cac_ratio",
                                  "payback_months",
                                  "rule_of_40",
                                  "nrr_pct",
                                  "gross_margin_pct",
                                  "mrr_growth_pct"
                                ],
                                "description": "Which metric needs attention."
                              },
                              "action": {
                                "type": "string",
                                "description": "The specific remediation for that metric."
                              }
                            }
                          }
                        },
                        "strengths": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Messages for metrics that came back healthy, passing or excellent."
                        }
                      }
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/sprint-health": {
      "post": {
        "operationId": "sprint_health",
        "summary": "Sprint/delivery health advisor",
        "description": "Sprint/delivery health advisor. POST 1-24 sprints (committed/completed points, optional per-ticket estimate-vs-actual data) and get computed metrics each with a good/warn/bad health tag: velocity avg + trend + stability, commitment reliability, spillover rate, mid-sprint scope change, estimate accuracy (median + chronic-overrun pct), and WIP concentration per assignee. Plus an overall grade and a ranked what-to-fix-first focus list. Pure math + advisory, no LLM.\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "sprint_health",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sprints"
                ],
                "properties": {
                  "sprints": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "description": "Oldest first: [{name, committed_points, completed_points, added_points?, spillover_points?, tickets?: [{key, estimate_hours, actual_hours, status, assignee}]}]."
                  },
                  "team_size": {
                    "type": "number"
                  },
                  "sprint_length_days": {
                    "type": "number",
                    "description": "Default 10."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "metrics",
                    "grade",
                    "focus",
                    "insufficient",
                    "context",
                    "request_id"
                  ],
                  "properties": {
                    "grade": {
                      "type": "string",
                      "enum": [
                        "A",
                        "B",
                        "C",
                        "D",
                        "F"
                      ],
                      "description": "Mean of every health check, where good scores 1, warn 0.5 and bad 0. A at 0.90, B at 0.75, C at 0.60, D at 0.45. Note that a run with NO computable checks grades F rather than being ungraded."
                    },
                    "metrics": {
                      "type": "object",
                      "description": "One entry per computable metric. A metric whose inputs were missing is ABSENT from this object entirely rather than null - check `insufficient` to find out which and why.",
                      "properties": {
                        "velocity": {
                          "allOf": [
                            {
                              "$ref": "#/components/schemas/sprint_health_metric"
                            }
                          ],
                          "description": "Completed points per sprint, plus trend and stability.",
                          "properties": {
                            "value": {
                              "type": "object",
                              "properties": {
                                "per_sprint": {
                                  "type": "array",
                                  "items": {
                                    "type": "number"
                                  },
                                  "description": "Completed points, oldest first."
                                },
                                "avg": {
                                  "type": "number",
                                  "description": "Mean completed points per sprint."
                                },
                                "trend_pct": {
                                  "type": [
                                    "number",
                                    "null"
                                  ],
                                  "description": "Recent multi-sprint average against the prior one, as a percentage change. Null when there is too little history."
                                },
                                "stability_cv": {
                                  "type": [
                                    "number",
                                    "null"
                                  ],
                                  "description": "Coefficient of variation of completed points. Lower is steadier. Null with fewer than 2 sprints."
                                }
                              }
                            }
                          }
                        },
                        "commitment_reliability": {
                          "allOf": [
                            {
                              "$ref": "#/components/schemas/sprint_health_metric"
                            }
                          ],
                          "description": "Completed divided by committed. Sprints that committed zero points are skipped rather than counted as failures.",
                          "properties": {
                            "value": {
                              "$ref": "#/components/schemas/sprint_health_ratioValue"
                            }
                          }
                        },
                        "spillover_rate": {
                          "allOf": [
                            {
                              "$ref": "#/components/schemas/sprint_health_metric"
                            }
                          ],
                          "description": "Share of committed work carried into the next sprint.",
                          "properties": {
                            "value": {
                              "$ref": "#/components/schemas/sprint_health_ratioValue"
                            }
                          }
                        },
                        "scope_change_rate": {
                          "allOf": [
                            {
                              "$ref": "#/components/schemas/sprint_health_metric"
                            }
                          ],
                          "description": "Share of work added after the sprint started.",
                          "properties": {
                            "value": {
                              "$ref": "#/components/schemas/sprint_health_ratioValue"
                            }
                          }
                        },
                        "estimate_accuracy": {
                          "allOf": [
                            {
                              "$ref": "#/components/schemas/sprint_health_metric"
                            }
                          ],
                          "description": "Actual hours against estimated hours. Requires per-ticket data on the submitted sprints.",
                          "properties": {
                            "value": {
                              "type": "object",
                              "properties": {
                                "median_ratio": {
                                  "type": "number",
                                  "description": "Median actual/estimate. 1.0 is perfect; above 1 means chronic underestimation."
                                },
                                "p90_ratio": {
                                  "type": "number",
                                  "description": "90th-percentile ratio - how bad the worst cases get."
                                },
                                "chronic_over_pct": {
                                  "type": "number",
                                  "description": "Share of tickets exceeding the chronic-overrun multiple."
                                },
                                "sample_size": {
                                  "type": "integer",
                                  "description": "Tickets that had usable estimate and actual values."
                                },
                                "excluded_count": {
                                  "type": "integer",
                                  "description": "Tickets dropped for missing or invalid hours."
                                }
                              }
                            }
                          }
                        },
                        "wip_concentration": {
                          "allOf": [
                            {
                              "$ref": "#/components/schemas/sprint_health_metric"
                            }
                          ],
                          "description": "How much of the load sits with one person - the bus-factor signal.",
                          "properties": {
                            "value": {
                              "type": "object",
                              "properties": {
                                "top_assignee": {
                                  "type": "string",
                                  "description": "Whoever carries the most estimated load."
                                },
                                "top_assignee_share": {
                                  "type": "number",
                                  "minimum": 0,
                                  "maximum": 1,
                                  "description": "Their share of the total, as a fraction."
                                },
                                "assignee_count": {
                                  "type": "integer",
                                  "description": "How many people carried work."
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "focus": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "What to fix, worst first: bad before warn, and within each by size of deviation. Empty when everything is good."
                    },
                    "insufficient": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Metrics or sub-metrics that could not be computed for lack of data, named in dotted form.",
                      "example": [
                        "velocity.trend_pct",
                        "estimate_accuracy"
                      ]
                    },
                    "context": {
                      "type": "object",
                      "description": "What the numbers were computed against.",
                      "required": [
                        "sprint_count",
                        "sprint_length_days"
                      ],
                      "properties": {
                        "sprint_count": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 24,
                          "description": "How many sprints were analysed."
                        },
                        "sprint_length_days": {
                          "type": "integer",
                          "description": "Sprint length used, defaulting to 10."
                        },
                        "team_size": {
                          "type": "integer",
                          "description": "Present only when you supplied it."
                        },
                        "avg_points_per_member": {
                          "type": "number",
                          "description": "Present only when team_size was supplied."
                        }
                      }
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/ticket-lint": {
      "post": {
        "operationId": "ticket_lint",
        "summary": "Full Jira-ticket quality linter (whole ticket, not just AC - use acceptance_criteria_validator for the deep AC pass)",
        "description": "Full Jira-ticket quality linter (whole ticket, not just AC - use acceptance_criteria_validator for the deep AC pass). POST one ticket or a batch (max 50) and get per-ticket findings with severity + concrete fixes: missing/weak summary or description, missing AC or estimate, bugs without repro/expected-vs-actual/environment, missing epic link, points-vs-hours mismatch, oversized estimates to split, urgent-but-thin tickets. Returns score, grade, and a ready/needs_work verdict per ticket plus batch summary. Type-aware rules (story/bug/task/epic/subtask), strict profile available. Deterministic, no LLM.\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "ticket_lint",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ticket": {
                    "type": "object",
                    "description": "One ticket: {key, summary, description, acceptance_criteria, issue_type, estimate_hours|story_points, priority, labels, components, epic_link, ...}. Missing fields ARE findings."
                  },
                  "tickets": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "description": "Alternative: batch of up to 50 tickets."
                  },
                  "profile": {
                    "type": "string",
                    "enum": [
                      "normal",
                      "strict"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "description": "A single `ticket` returns that ticket's result at the top level; a `tickets` batch returns `results` plus a batch `summary`. Detect the batch form by the presence of `results`.",
                  "oneOf": [
                    {
                      "type": "object",
                      "title": "Single ticket",
                      "required": [
                        "key",
                        "score_0_100",
                        "grade",
                        "verdict",
                        "findings",
                        "request_id"
                      ],
                      "properties": {
                        "key": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "The ticket key you supplied, or null if it had none.",
                          "example": "PROJ-142"
                        },
                        "score_0_100": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 100,
                          "description": "Starts at 100 and deducts 20 per high finding, 8 per medium, 3 per low."
                        },
                        "grade": {
                          "type": "string",
                          "enum": [
                            "A",
                            "B",
                            "C",
                            "D",
                            "F"
                          ],
                          "description": "A at 90, B at 80, C at 70, D at 60, F below."
                        },
                        "verdict": {
                          "type": "string",
                          "enum": [
                            "ready",
                            "needs_work"
                          ],
                          "description": "Ready requires the score to clear the profile threshold (75 normal, 85 strict) AND zero high-severity findings. A single high finding blocks regardless of score."
                        },
                        "findings": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ticket_lint_finding"
                          },
                          "description": "Problems found, most severe first, then by field and code so ordering is stable."
                        },
                        "request_id": {
                          "type": "string",
                          "description": "Identifier for this metered call."
                        }
                      }
                    },
                    {
                      "type": "object",
                      "title": "Batch of tickets",
                      "required": [
                        "results",
                        "summary",
                        "request_id"
                      ],
                      "properties": {
                        "results": {
                          "type": "array",
                          "description": "One result per submitted ticket, in submission order.",
                          "items": {
                            "type": "object",
                            "required": [
                              "key",
                              "score_0_100",
                              "grade",
                              "verdict",
                              "findings"
                            ],
                            "properties": {
                              "key": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "Ticket key, or null."
                              },
                              "score_0_100": {
                                "type": "integer",
                                "minimum": 0,
                                "maximum": 100,
                                "description": "This ticket's score."
                              },
                              "grade": {
                                "type": "string",
                                "enum": [
                                  "A",
                                  "B",
                                  "C",
                                  "D",
                                  "F"
                                ],
                                "description": "Letter grade for this ticket."
                              },
                              "verdict": {
                                "type": "string",
                                "enum": [
                                  "ready",
                                  "needs_work"
                                ],
                                "description": "Whether this ticket is ready to work."
                              },
                              "findings": {
                                "type": "array",
                                "items": {
                                  "$ref": "#/components/schemas/ticket_lint_finding"
                                },
                                "description": "Problems found on this ticket."
                              }
                            }
                          }
                        },
                        "summary": {
                          "type": "object",
                          "required": [
                            "avg_score",
                            "ready_count",
                            "needs_work_count",
                            "worst"
                          ],
                          "properties": {
                            "avg_score": {
                              "type": "number",
                              "description": "Mean score across the batch, to one decimal. Zero for an empty batch."
                            },
                            "ready_count": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "How many tickets are ready to work."
                            },
                            "needs_work_count": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "How many need revision."
                            },
                            "worst": {
                              "type": "array",
                              "maxItems": 5,
                              "items": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "Ticket key, or null when that ticket had no key."
                              },
                              "description": "Up to five lowest-scoring tickets needing work, worst first. Where to start a grooming session."
                            }
                          }
                        },
                        "request_id": {
                          "type": "string",
                          "description": "Identifier for this metered call."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/uploads": {
      "post": {
        "operationId": "uploads",
        "summary": "Get a presigned S3 PUT URL to upload a file (policy doc, config, log, up to 5MB) that a file-ingesting endpoint then reads by upload_id",
        "description": "Get a presigned S3 PUT URL to upload a file (policy doc, config, log, up to 5MB) that a file-ingesting endpoint then reads by upload_id. Two-step: call this, PUT your file to the returned url, then pass upload_id to the consuming endpoint. Uploads auto-expire in 1 day and are readable only by your key. Auth required; not billed.\n\nThis endpoint is authenticated and rate limited but **not metered** - it costs no credits.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "uploads",
        "x-credit-cost": 0,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "content_type": {
                    "type": "string",
                    "description": "MIME type of the file you will PUT (e.g. text/plain, application/json). Default application/octet-stream."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "upload_id",
                    "put_url",
                    "expires_in",
                    "max_bytes",
                    "content_type"
                  ],
                  "properties": {
                    "upload_id": {
                      "type": "string",
                      "description": "Pass this to a file-ingesting endpoint once the PUT has completed. Scoped to your key - another key cannot read it.",
                      "example": "uploads/key_1a2b3c4d5e6f7a8b/9f8e7d6c5b4a39281706"
                    },
                    "put_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "Presigned S3 URL. Send the file body with HTTP PUT and a Content-Type header matching content_type exactly, or S3 rejects the signature. Do not send your API key to this URL.",
                      "example": "https://example-bucket.s3.amazonaws.com/uploads/...&X-Amz-Signature=EXAMPLE"
                    },
                    "expires_in": {
                      "type": "integer",
                      "description": "Seconds the presigned URL stays valid. Currently 900 (15 minutes).",
                      "example": 900
                    },
                    "max_bytes": {
                      "type": "integer",
                      "description": "Largest file the consuming endpoint will accept. Currently 5,000,000.",
                      "example": 5000000
                    },
                    "content_type": {
                      "type": "string",
                      "description": "Echo of the content type the URL was signed for.",
                      "example": "application/json"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/worklog-guardrail": {
      "post": {
        "operationId": "worklog_guardrail",
        "summary": "Worklog/timesheet policy checker - run a batch of worklog entries through configurable guardrails BEFORE posting them to a time-tracking system",
        "description": "Worklog/timesheet policy checker - run a batch of worklog entries through configurable guardrails BEFORE posting them to a time-tracking system. Checks: rounding increment (default 0.25h, float-safe), per-entry min/max, per-author daily totals, duplicates, over-estimate alerts (never suggests inflating hours or estimates - flags to PM instead), future dates, weekends, immutable-author edit protection, missing description/ticket. Returns pass/warnings/blocked verdict + per-entry violations with fixes + hour totals by author and date. Caller supplies the reference date - fully deterministic, no LLM.\n\nCosts **1 credit** per successful call.",
        "tags": [
          "Endpoints"
        ],
        "x-mcp-tool": "worklog_guardrail",
        "x-credit-cost": 1,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "entries",
                  "today"
                ],
                "properties": {
                  "entries": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "description": "Up to 500: [{author, date \"YYYY-MM-DD\", hours, ticket?, description?, estimate_hours?, already_logged_hours?, id?, edit?, delete?, new?}]."
                  },
                  "today": {
                    "type": "string",
                    "description": "Reference date \"YYYY-MM-DD\" for future-date checks."
                  },
                  "policy": {
                    "type": "object",
                    "description": "Optional overrides: rounding_increment, max_hours_per_day, max_hours_per_entry, min_hours_per_entry, immutable_authors, require_description, require_ticket, over_estimate_policy (alert|block|ignore), future_dates (block|alert), weekend_policy (allow|alert)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls permitted in the current window."
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Calls left in the current window."
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix time at which the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "verdict",
                    "violations",
                    "summary",
                    "policy_applied",
                    "request_id"
                  ],
                  "properties": {
                    "verdict": {
                      "type": "string",
                      "enum": [
                        "pass",
                        "warnings",
                        "blocked"
                      ],
                      "description": "Worst severity present: \"blocked\" if any violation blocks, \"warnings\" if any alerts, otherwise \"pass\". Treat \"blocked\" as do-not-post."
                    },
                    "violations": {
                      "type": "array",
                      "description": "Every problem found, sorted by entry index and then by a fixed code order so output is stable across runs. One entry can produce several violations.",
                      "items": {
                        "type": "object",
                        "required": [
                          "entry_index",
                          "entry_id",
                          "author",
                          "code",
                          "severity",
                          "message",
                          "fix"
                        ],
                        "properties": {
                          "entry_index": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Position of the offending entry in your submitted list."
                          },
                          "entry_id": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "The entry's own id, or null if it had none."
                          },
                          "author": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Author of the entry, or null when that is what is missing."
                          },
                          "code": {
                            "type": "string",
                            "enum": [
                              "malformed_date",
                              "missing_author",
                              "invalid_hours",
                              "rounding",
                              "min_hours",
                              "max_hours",
                              "missing_description",
                              "missing_ticket",
                              "future_date",
                              "weekend_entry",
                              "immutable_author",
                              "over_estimate",
                              "daily_total_exceeded",
                              "possible_duplicate"
                            ],
                            "description": "Which guardrail fired. Stable identifier - branch on this rather than on the message text."
                          },
                          "severity": {
                            "type": "string",
                            "enum": [
                              "block",
                              "alert"
                            ],
                            "description": "\"block\" must be resolved before posting; \"alert\" is advisory."
                          },
                          "message": {
                            "type": "string",
                            "description": "What is wrong, including the offending values."
                          },
                          "fix": {
                            "type": "string",
                            "description": "The concrete remediation. Note that over_estimate never suggests inflating an estimate or hours - it directs the discrepancy to a PM instead."
                          }
                        }
                      }
                    },
                    "summary": {
                      "type": "object",
                      "description": "Totals across the whole batch, useful for a pre-post confirmation screen.",
                      "required": [
                        "entries",
                        "blocked",
                        "alerts",
                        "total_hours",
                        "by_author",
                        "by_date"
                      ],
                      "properties": {
                        "entries": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "How many entries were checked."
                        },
                        "blocked": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Count of blocking violations, not of blocked entries - one entry may contribute several."
                        },
                        "alerts": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Count of advisory violations."
                        },
                        "total_hours": {
                          "type": "number",
                          "description": "Sum of all valid hours in the batch."
                        },
                        "by_author": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "number"
                          },
                          "description": "Total hours keyed by author name.",
                          "example": {
                            "alex": 7.5,
                            "sam": 6.25
                          }
                        },
                        "by_date": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "number"
                          },
                          "description": "Total hours keyed by ISO date.",
                          "example": {
                            "2026-07-21": 13.75
                          }
                        }
                      }
                    },
                    "policy_applied": {
                      "type": "object",
                      "description": "The policy actually used, your overrides merged over the defaults. Echoed so a rejected batch can be explained without guessing which rules were in force.",
                      "required": [
                        "rounding_increment",
                        "max_hours_per_day",
                        "max_hours_per_entry",
                        "min_hours_per_entry",
                        "immutable_authors",
                        "require_description",
                        "require_ticket",
                        "over_estimate_policy",
                        "future_dates",
                        "weekend_policy"
                      ],
                      "properties": {
                        "rounding_increment": {
                          "type": "number",
                          "description": "Hours must be a multiple of this. Default 0.25.",
                          "example": 0.25
                        },
                        "max_hours_per_day": {
                          "type": "number",
                          "description": "Per author per day. Default 10."
                        },
                        "max_hours_per_entry": {
                          "type": "number",
                          "description": "Single entry ceiling. Default 8."
                        },
                        "min_hours_per_entry": {
                          "type": "number",
                          "description": "Single entry floor. Default 0.25."
                        },
                        "immutable_authors": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Authors whose existing entries may not be edited or deleted."
                        },
                        "require_description": {
                          "type": "boolean",
                          "description": "Whether a description is mandatory. Default false."
                        },
                        "require_ticket": {
                          "type": "boolean",
                          "description": "Whether a ticket reference is mandatory. Default false."
                        },
                        "over_estimate_policy": {
                          "type": "string",
                          "enum": [
                            "alert",
                            "block",
                            "ignore"
                          ],
                          "description": "How to treat logging past the estimate. Default alert."
                        },
                        "future_dates": {
                          "type": "string",
                          "enum": [
                            "block",
                            "alert"
                          ],
                          "description": "How to treat dates after `today`. Default block."
                        },
                        "weekend_policy": {
                          "type": "string",
                          "enum": [
                            "allow",
                            "alert"
                          ],
                          "description": "How to treat weekend entries. Default allow."
                        }
                      }
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Identifier for this metered call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/keys": {
      "post": {
        "operationId": "issueKey",
        "summary": "Issue an API key (admin only)",
        "description": "Mint a new API key with a starting credit balance. Requires a key with the `admin` scope, so this is an operator endpoint, not a signup endpoint - `admin` itself cannot be granted through this API. The token is returned exactly once and only its hash is stored; if it is lost, revoke and reissue. Not metered.",
        "tags": [
          "Administration"
        ],
        "x-admin-only": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "label"
                ],
                "properties": {
                  "label": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Human-readable name for the key, for your own records."
                  },
                  "credits": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Starting credit balance. Floored to an integer; defaults to 0."
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Scopes to grant. Defaults to [\"call\"]. \"admin\" is rejected."
                  },
                  "plan": {
                    "type": "string",
                    "description": "Plan label recorded on the key. Defaults to \"beta\"."
                  },
                  "stripe_customer_id": {
                    "type": "string",
                    "description": "Optional Stripe customer to meter this key's usage against."
                  },
                  "beta_flags": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Optional per-key feature flags."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Key created. This is the only time `token` is ever returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "key_id",
                    "token",
                    "credits",
                    "scopes"
                  ],
                  "properties": {
                    "key_id": {
                      "type": "string",
                      "description": "Stable identifier for the key.",
                      "example": "key_1a2b3c4d5e6f7a8b"
                    },
                    "token": {
                      "type": "string",
                      "description": "The secret key. Shown once; store it now.",
                      "example": "qlk_YOUR_API_KEY"
                    },
                    "credits": {
                      "type": "integer",
                      "description": "Starting credit balance."
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Scopes granted to the key."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "mcpJsonRpc",
        "summary": "MCP endpoint (JSON-RPC 2.0 over Streamable HTTP)",
        "description": "Model Context Protocol server exposing every tool on this platform to an MCP client. Stateless: no session is established, so a client may send each request independently. Supported methods are `initialize`, `notifications/initialized`, `ping`, `tools/list` and `tools/call`; a JSON-RPC batch (an array of requests) is accepted.\n\n`tools/list` returns only tools whose endpoint is currently deployed and enabled, and `tools/call` on a disabled tool returns an explicit `endpoint_disabled` result rather than a transport-level 404.\n\n`tools/call` forwards your Bearer key to the underlying endpoint, so auth, rate limiting and credit metering apply exactly as they do over REST. The gateway itself is not metered - you are never charged twice for one call.",
        "tags": [
          "MCP"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response. Note that tool-level failures are reported in `result.isError`, not as an HTTP error status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "202": {
            "description": "Accepted. Returned for notifications, which have no response body."
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    }
  }
}
