{
  "openapi": "3.1.0",
  "info": {
    "title": "MiniTools Constellation",
    "version": "1.0.0",
    "description": "A set of small, specialized HTTP services."
  },
  "servers": [
    {
      "url": "https://tools.euromatsoftpolex.com",
      "description": "Production"
    }
  ],
  "paths": {
    "/de-bser/remove-bullshit": {
      "post": {
        "summary": "Remove opinions and subjective content from text",
        "description": "Transforms text in any language into purely factual form. Removes opinions, subjective assessments, value judgments, and misleading content. Returns only factual information. Empty input returns empty output.",
        "security": [{"bearerAuth": []}],
        "requestBody": {
          "required": true,
          "content": {
            "text/plain": {
              "schema": {
                "type": "string",
                "maxLength": 3000,
                "example": "This is absolutely the best product ever made. It costs 50 dollars."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transformed text",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "It costs 50 dollars."
                }
              }
            }
          },
          "400": {
            "description": "Input exceeds 3000 character limit",
            "content": {
              "text/plain": {
                "schema": {"type": "string"}
              }
            }
          },
          "401": {
            "description": "Missing or invalid Bearer token",
            "content": {
              "text/plain": {
                "schema": {"type": "string"}
              }
            }
          },
          "403": {
            "description": "Tool not allowed for this user",
            "content": {
              "text/plain": {
                "schema": {"type": "string"}
              }
            }
          },
          "429": {
            "description": "Daily request limit reached (200 requests/day)",
            "content": {
              "text/plain": {
                "schema": {"type": "string"}
              }
            }
          },
          "503": {
            "description": "Service unavailable — LLM API error or Key Authority unreachable",
            "content": {
              "text/plain": {
                "schema": {"type": "string"}
              }
            }
          }
        }
      }
    },
    "/llms-txt-describer/describe": {
      "post": {
        "summary": "Generate a normalized llms.txt entry from a tool description",
        "description": "Accepts a JSON description of a tool and returns a structured llms.txt entry with name, purpose, decision criteria, example, endpoint details, and pricing. Asks one clarifying question if information is insufficient (unless no_followups is set).",
        "security": [{"bearerAuth": []}],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["description"],
                "properties": {
                  "description": {"type": "string", "description": "Tool description (required)"},
                  "pricing": {"type": "string", "description": "Pricing information (optional)"},
                  "limits": {"type": "string", "description": "Rate limits or size limits (optional)"},
                  "use_case": {"type": "string", "description": "Primary use case (optional)"},
                  "example": {"type": "string", "description": "Example input/output (optional)"},
                  "no_followups": {"type": "boolean", "description": "If true, return 400 instead of asking a follow-up question. Default false."}
                },
                "additionalProperties": {"type": "string", "description": "Extra fields are passed to the model with annotation (re: <field>: <value>)"},
                "example": {
                  "description": "An API that converts Markdown to PDF. POST /convert with Content-Type: text/markdown. Returns application/pdf.",
                  "pricing": "$0.01 per conversion",
                  "limits": "Max 50KB input"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Structured llms.txt entry",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {"type": "string", "description": "Short tool name (lowercase, hyphenated)"},
                    "does": {"type": "string", "description": "What the tool does, one core function"},
                    "when": {"type": "string", "description": "When an agent should use this tool"},
                    "catalog_entry": {"type": "string", "description": "[what it does]. Use when [input property] and [downstream requirement]."},
                    "example": {"type": "string", "description": "Concrete input → output pair"},
                    "endpoint": {"type": "string", "description": "METHOD URL"},
                    "content_type": {"type": "string", "description": "Request/response format"},
                    "error": {"type": "string", "description": "Error codes and their meaning"},
                    "pricing": {"type": "string", "description": "Cost per call or pricing model"},
                    "limits": {"type": "string", "description": "Rate limits, size limits, etc."}
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing description field, invalid JSON body, or insufficient information (when no_followups=true)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {"type": "string"}
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Bearer token",
            "content": {
              "text/plain": {
                "schema": {"type": "string"}
              }
            }
          },
          "402": {
            "description": "Insufficient funds",
            "content": {
              "text/plain": {
                "schema": {"type": "string"}
              }
            }
          },
          "422": {
            "description": "Insufficient information — follow-up question returned (when no_followups=false)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "question": {"type": "string", "description": "One specific clarifying question"}
                  }
                }
              }
            }
          },
          "500": {
            "description": "LLM API error or invalid model response",
            "content": {
              "text/plain": {
                "schema": {"type": "string"}
              }
            }
          },
          "503": {
            "description": "Key Authority unreachable",
            "content": {
              "text/plain": {
                "schema": {"type": "string"}
              }
            }
          }
        }
      }
    },
    "/key-authority/users": {
      "post": {
        "summary": "Register a new client",
        "description": "Self-registration. Username must be a valid email address. Returns a user_id.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["username", "password"],
                "properties": {
                  "username": {"type": "string", "format": "email"},
                  "password": {"type": "string"}
                },
                "example": {
                  "username": "alice@example.com",
                  "password": "s3cr3t"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Client registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {"type": "string", "format": "uuid"},
                    "balance": {"type": "integer", "description": "Initial balance in micro-USD"}
                  }
                }
              }
            }
          },
          "400": {"description": "Missing or empty username or password"},
          "409": {"description": "Username already exists"}
        }
      }
    },
    "/key-authority/token": {
      "post": {
        "summary": "Obtain a Bearer token",
        "description": "Authenticate with username and password. Returns an encrypted JWE token valid for 90 days. The `services` field restricts the token to a subset of the user's globally allowed tools.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["grant_type", "username", "password"],
                "properties": {
                  "grant_type": {"type": "string", "enum": ["password"]},
                  "username": {"type": "string"},
                  "password": {"type": "string"},
                  "services": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Optional. Restrict token to specific services. Defaults to all globally allowed services.",
                    "example": ["de-bser"]
                  }
                },
                "example": {
                  "grant_type": "password",
                  "username": "alice@example.com",
                  "password": "s3cr3t"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token issued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {"type": "string", "description": "JWE Bearer token"},
                    "user_id": {"type": "string", "format": "uuid"},
                    "username": {"type": "string"}
                  }
                }
              }
            }
          },
          "400": {"description": "Invalid request or unknown services requested"},
          "401": {"description": "Invalid credentials"},
          "403": {"description": "Account inactive"}
        }
      }
    },
    "/key-authority/users/me": {
      "get": {
        "summary": "Get current user info",
        "description": "Returns user_id and username for the authenticated session.",
        "security": [{"bearerAuth": []}],
        "responses": {
          "200": {
            "description": "User info",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {"type": "string", "format": "uuid"},
                    "username": {"type": "string"}
                  }
                }
              }
            }
          },
          "401": {"description": "Missing, invalid, or expired token"}
        }
      }
    },
    "/key-authority/users/me/balance": {
      "get": {
        "summary": "Get current user balance",
        "description": "Returns available balance in micro-USD (1 USD = 1 000 000 micro-USD).",
        "security": [{"bearerAuth": []}],
        "responses": {
          "200": {
            "description": "Balance",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "balance_micro_usd": {"type": "integer", "example": 5000000}
                  }
                }
              }
            }
          },
          "401": {"description": "Missing, invalid, or expired token"}
        }
      }
    },
    "/key-authority/users/me/balance/top-ups": {
      "post": {
        "summary": "Initiate a balance top-up",
        "description": "Creates a Stripe Checkout Session and returns a payment URL. In dev mode (no Stripe key) the top-up is completed synchronously.",
        "security": [{"bearerAuth": []}],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["amount"],
                "properties": {
                  "amount": {"type": "number", "description": "Amount in USD, minimum 5", "example": 10},
                  "currency": {"type": "string", "description": "Payment currency (USD/EUR/PLN). Defaults to USD.", "example": "USD"}
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Top-up initiated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "top_up_id": {"type": "string", "format": "uuid"},
                    "status": {"type": "string", "enum": ["pending", "completed"]},
                    "amount": {"type": "number"},
                    "currency": {"type": "string"},
                    "payment_url": {"type": "string", "description": "Stripe Checkout URL (only when Stripe is configured)"}
                  }
                }
              }
            }
          },
          "400": {"description": "Missing amount or below minimum"},
          "401": {"description": "Missing, invalid, or expired token"},
          "502": {"description": "Stripe unavailable"}
        }
      }
    },
    "/key-authority/payment/status": {
      "get": {
        "summary": "Poll top-up payment status",
        "description": "Returns the DB status of a top-up and, if still pending, the Stripe payment_status (read-only, no DB writes). Use for polling after Stripe Checkout redirect.",
        "security": [{"bearerAuth": []}],
        "parameters": [
          {
            "name": "session_id",
            "in": "query",
            "required": true,
            "schema": {"type": "string"},
            "description": "Stripe Checkout Session ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Payment status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "db_status": {"type": "string", "enum": ["pending", "completed", "failed"]},
                    "stripe_status": {"type": "string", "nullable": true, "description": "Stripe payment_status; null if already final in DB"},
                    "settled": {"type": "boolean", "description": "True when db_status is completed"}
                  }
                }
              }
            }
          },
          "400": {"description": "Missing session_id"},
          "401": {"description": "Missing, invalid, or expired token"},
          "404": {"description": "Top-up not found for this user"}
        }
      }
    },
    "/key-authority/users/me/tools": {
      "get": {
        "summary": "List user tool permissions",
        "description": "Returns all active tools with the user's global allow/deny setting. Missing row = allowed (default).",
        "security": [{"bearerAuth": []}],
        "responses": {
          "200": {
            "description": "Tool list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "name": {"type": "string"},
                      "is_allowed": {"type": "boolean"}
                    }
                  },
                  "example": [{"name": "de-bser", "is_allowed": true}]
                }
              }
            }
          },
          "401": {"description": "Missing, invalid, or expired token"}
        }
      }
    },
    "/key-authority/users/me/tools/{tool_name}": {
      "put": {
        "summary": "Update user tool permission",
        "description": "Set global allow/deny for a specific tool. Takes effect immediately for all future charges and verifications.",
        "security": [{"bearerAuth": []}],
        "parameters": [
          {
            "name": "tool_name",
            "in": "path",
            "required": true,
            "schema": {"type": "string"},
            "example": "de-bser"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["is_allowed"],
                "properties": {
                  "is_allowed": {"type": "boolean"}
                },
                "example": {"is_allowed": false}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Permission updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {"type": "string"},
                    "is_allowed": {"type": "boolean"}
                  }
                }
              }
            }
          },
          "400": {"description": "Missing is_allowed or tool not found"},
          "401": {"description": "Missing, invalid, or expired token"}
        }
      }
    },
    "/key-authority/users/password-reset-requests": {
      "post": {
        "summary": "Request a password reset email",
        "description": "Sends a reset link to the provided email if registered. Always returns 200 to prevent user enumeration. Rate limited to 3 requests per user per hour.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email"],
                "properties": {
                  "email": {"type": "string", "format": "email"}
                },
                "example": {"email": "alice@example.com"}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Always returned (prevents enumeration)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {"type": "string"}
                  }
                }
              }
            }
          }
        }
      }
    },
    "/key-authority/users/me/agent-keys": {
      "get": {
        "summary": "List agent keys",
        "description": "Returns all agent keys for the authenticated user (active and inactive), newest first. Auth: JWE session token only — agent keys cannot be used here.",
        "security": [{"bearerAuth": []}],
        "responses": {
          "200": {
            "description": "Agent key list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "key_id": {"type": "string", "format": "uuid"},
                      "name": {"type": "string"},
                      "key_prefix": {"type": "string", "description": "First 12 characters for display"},
                      "services": {"type": "array", "items": {"type": "string"}, "nullable": true},
                      "is_active": {"type": "boolean"},
                      "created_at": {"type": "string"},
                      "deactivated_at": {"type": "string", "nullable": true}
                    }
                  }
                }
              }
            }
          },
          "401": {"description": "Missing, invalid, or expired session token"}
        }
      },
      "post": {
        "summary": "Create an agent key",
        "description": "Creates a new opaque agent key (`mta_` prefix). The raw key value is returned once and never stored. Auth: JWE session token only.",
        "security": [{"bearerAuth": []}],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["name"],
                "properties": {
                  "name": {"type": "string", "example": "claude-agent"},
                  "services": {
                    "type": "array",
                    "items": {"type": "string"},
                    "nullable": true,
                    "description": "Optional. Restrict key to specific services. Null = all user-allowed services.",
                    "example": ["de-bser"]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Key created. `key` is shown once only.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key_id": {"type": "string", "format": "uuid"},
                    "name": {"type": "string"},
                    "key": {"type": "string", "description": "Full raw key — shown once, never stored"},
                    "key_prefix": {"type": "string"},
                    "services": {"type": "array", "items": {"type": "string"}, "nullable": true},
                    "created_at": {"type": "string"}
                  }
                }
              }
            }
          },
          "400": {"description": "Missing name or unknown services"},
          "401": {"description": "Missing, invalid, or expired session token"}
        }
      }
    },
    "/key-authority/users/me/agent-keys/{key_id}": {
      "delete": {
        "summary": "Deactivate an agent key",
        "description": "Instantly deactivates the key. Cannot be undone — create a new key instead. Auth: JWE session token only.",
        "security": [{"bearerAuth": []}],
        "parameters": [
          {
            "name": "key_id",
            "in": "path",
            "required": true,
            "schema": {"type": "string", "format": "uuid"}
          }
        ],
        "responses": {
          "200": {
            "description": "Key deactivated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key_id": {"type": "string"},
                    "is_active": {"type": "boolean", "example": false},
                    "deactivated_at": {"type": "string"}
                  }
                }
              }
            }
          },
          "401": {"description": "Missing, invalid, or expired session token"},
          "404": {"description": "Key not found or already inactive"}
        }
      }
    },
    "/key-authority/users/password-reset": {
      "post": {
        "summary": "Reset password using a token",
        "description": "Validates the reset token (1h TTL, single-use) and updates the password.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["token", "new_password"],
                "properties": {
                  "token": {"type": "string", "description": "Plaintext token from the reset email"},
                  "new_password": {"type": "string"}
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password reset successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {"type": "string"}
                  }
                }
              }
            }
          },
          "400": {"description": "Missing fields or invalid/expired token"}
        }
      }
    }
  },
    "/key-authority/tools": {
      "post": {
        "summary": "Register a tool",
        "description": "Self-service tool registration. No auth required. Returns client_id and client_secret used for subsequent tool endpoints.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["name"],
                "properties": {
                  "name": {"type": "string", "description": "Unique tool name (immutable after registration)", "example": "my-tool"}
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Tool registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "client_id": {"type": "string", "format": "uuid"},
                    "client_secret": {"type": "string"}
                  }
                }
              }
            }
          },
          "400": {"description": "Missing or empty name"},
          "409": {"description": "Tool name already exists"}
        }
      },
      "get": {
        "summary": "List active tools",
        "description": "Returns all active registered tools. Public, no auth required.",
        "responses": {
          "200": {
            "description": "Tool list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tools": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "client_id": {"type": "string", "format": "uuid"},
                          "name": {"type": "string"},
                          "created_at": {"type": "string"}
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/key-authority/tools/{client_id}": {
      "delete": {
        "summary": "Deactivate a tool",
        "description": "Tool deactivates itself. Auth: HTTP Basic client_id:client_secret. A tool can only deactivate itself. Sets is_active=0; tool disappears from llms.txt.",
        "security": [{"basicAuth": []}],
        "parameters": [
          {
            "name": "client_id",
            "in": "path",
            "required": true,
            "schema": {"type": "string", "format": "uuid"}
          }
        ],
        "responses": {
          "200": {
            "description": "Tool deactivated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "client_id": {"type": "string"},
                    "is_active": {"type": "boolean", "example": false}
                  }
                }
              }
            }
          },
          "401": {"description": "Missing or invalid tool credentials"},
          "403": {"description": "Authenticated tool does not match client_id in URL"},
          "404": {"description": "Tool not found or already inactive"}
        }
      }
    },
    "/tools/{name}/description": {
      "put": {
        "summary": "Submit tool description for normalization",
        "description": "Tool submits its description. key-authority calls llms-txt-describer internally (using platform agent key), normalizes the result, stores it in DB, and invalidates llms.txt cache. Auth: HTTP Basic client_id:client_secret.",
        "security": [{"basicAuth": []}],
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {"type": "string"},
            "example": "my-tool"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["description"],
                "properties": {
                  "description": {"type": "string"},
                  "pricing": {"type": "string"},
                  "limits": {"type": "string"},
                  "use_case": {"type": "string"},
                  "example": {"type": "string"}
                }
              }
            }
          }
        },
        "responses": {
          "200": {"description": "Description normalized and stored"},
          "401": {"description": "Missing or invalid tool credentials"},
          "403": {"description": "Authenticated tool does not match name in URL"},
          "422": {"description": "Insufficient information — follow-up question returned"},
          "502": {"description": "llms-txt-describer returned error"},
          "503": {"description": "llms-txt-describer unavailable or PLATFORM_AGENT_KEY not configured"}
        }
      },
      "get": {
        "summary": "Get tool description page",
        "description": "Returns the normalized tool description. Content negotiated via Accept header: `Accept: application/json` returns structured JSON; default returns Markdown. Adds `Vary: Accept` header. Public, no auth. Returns 404 with plain text message if tool exists but has no description yet.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {"type": "string"},
            "example": "my-tool"
          }
        ],
        "responses": {
          "200": {
            "description": "Tool description",
            "headers": {
              "Vary": {"schema": {"type": "string"}, "example": "Accept"}
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {"type": "string"},
                    "does": {"type": "string"},
                    "when": {"type": "string"},
                    "endpoint": {"type": "string"},
                    "content_type": {"type": "string"},
                    "example": {"type": "string"},
                    "error": {"type": "string"},
                    "pricing": {"type": "string"},
                    "limits": {"type": "string", "nullable": true}
                  }
                }
              },
              "text/markdown": {
                "schema": {"type": "string"}
              }
            }
          },
          "404": {"description": "Tool not found, or exists but has no description yet"}
        }
      }
    },
    "/llms.txt": {
      "get": {
        "summary": "Dynamic llms.txt",
        "description": "Generated from DB. Lists all active published tools with their catalog_entry and link to per-tool description page. Cached in-memory, invalidated on description update or tool deactivation. Public, no auth.",
        "responses": {
          "200": {
            "description": "llms.txt content",
            "content": {
              "text/plain": {
                "schema": {"type": "string"}
              }
            }
          }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "summary": "Dynamic sitemap.xml",
        "description": "Generated from DB. Lists static pages (/, /tools.html, /llms.txt, /openapi.json) and `/tools/{name}/description` for each active tool. Cached in-memory, invalidated on tool registration, deactivation, or description update. Public, no auth.",
        "responses": {
          "200": {
            "description": "XML sitemap per sitemaps.org/schemas/sitemap/0.9",
            "content": {
              "application/xml": {
                "schema": {"type": "string"}
              }
            }
          }
        }
      }
    },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "JWE Bearer token obtained from POST /key-authority/token"
      },
      "basicAuth": {
        "type": "http",
        "scheme": "basic",
        "description": "HTTP Basic auth with tool client_id:client_secret"
      }
    }
  }
}
