{
  "openapi": "3.1.0",
  "info": {
    "title": "BOZOS Agent Utility API",
    "version": "1.0.0",
    "description": "Small deterministic, stateless, side-effect-free utilities for agents. No authentication, retention, or outbound network requests. POST bodies are limited to 64 KiB. Questions and corrections: https://bozos.si/contact",
    "contact": {
      "name": "BOZOS",
      "email": "bozos@bozos.si",
      "url": "https://bozos.si/contact"
    },
    "license": {
      "name": "CC0-1.0",
      "identifier": "CC0-1.0"
    },
    "x-fair-use": {
      "requests_per_minute": 60,
      "burst": 10,
      "applies_to": [
        "/api/v1/*",
        "/api/mcp",
        "dataset and index files"
      ],
      "enforced_by_bozos": false,
      "status_when_limited": 429,
      "retry_after_on_429": "May be sent by the hosting platform; not guaranteed. Honor it when present.",
      "backoff_without_retry_after": "Exponential, starting at 1 second, at most 5 retries.",
      "prefer_conditional_requests": "Send If-None-Match with the ETag from content-index.json; a 304 costs almost nothing."
    }
  },
  "servers": [
    {
      "url": "https://bozos.si/api/v1"
    }
  ],
  "paths": {
    "/time": {
      "get": {
        "operationId": "timeInZones",
        "summary": "Convert a time between IANA time zones, or give the current time anywhere",
        "parameters": [
          {
            "name": "tz",
            "in": "query",
            "required": false,
            "description": "Comma-separated IANA zones (default UTC)",
            "schema": {
              "type": "string"
            },
            "example": "Asia/Tokyo,Europe/London"
          },
          {
            "name": "at",
            "in": "query",
            "required": false,
            "description": "ISO instant with Z/offset, or local wall time when from_tz is set",
            "schema": {
              "type": "string"
            },
            "example": "2026-11-03T09:00"
          },
          {
            "name": "from_tz",
            "in": "query",
            "required": false,
            "description": "Zone in which to interpret at",
            "schema": {
              "type": "string"
            },
            "example": "America/New_York"
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "text for just the one-line answer; json (default) for structured fields",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result with a one-line answer field",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "answer"
                  ],
                  "properties": {
                    "answer": {
                      "type": "string"
                    }
                  }
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; answer explains usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "answer": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "usage": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/date": {
      "get": {
        "operationId": "dateMath",
        "summary": "Weekday, ISO week, date addition, and day/weekday counts",
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": false,
            "description": "YYYY-MM-DD (default today, UTC)",
            "schema": {
              "type": "string"
            },
            "example": "2026-10-19"
          },
          {
            "name": "add",
            "in": "query",
            "required": false,
            "description": "Duration like 45d, -3w, 2m, 1y, 1y2m3d",
            "schema": {
              "type": "string"
            },
            "example": "90d"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Second date for differences",
            "schema": {
              "type": "string"
            },
            "example": "2026-12-25"
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "text for just the one-line answer; json (default) for structured fields",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result with a one-line answer field",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "answer"
                  ],
                  "properties": {
                    "answer": {
                      "type": "string"
                    }
                  }
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; answer explains usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "answer": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "usage": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/calc": {
      "get": {
        "operationId": "exactCalc",
        "summary": "Exact rational arithmetic with no floating-point error",
        "parameters": [
          {
            "name": "expr",
            "in": "query",
            "required": true,
            "description": "Expression using + - * / ^ ( ) decimals and e-notation; URL-encode + as %2B",
            "schema": {
              "type": "string"
            },
            "example": "0.1+0.2"
          },
          {
            "name": "digits",
            "in": "query",
            "required": false,
            "description": "Decimal places for non-terminating results (default 50, max 1000)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "text for just the one-line answer; json (default) for structured fields",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result with a one-line answer field",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "answer"
                  ],
                  "properties": {
                    "answer": {
                      "type": "string"
                    }
                  }
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; answer explains usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "answer": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "usage": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/count": {
      "get": {
        "operationId": "countText",
        "summary": "Count characters, words, bytes, and occurrences of a letter or substring",
        "parameters": [
          {
            "name": "text",
            "in": "query",
            "required": true,
            "description": "Text to measure (max 10,000 characters)",
            "schema": {
              "type": "string"
            },
            "example": "strawberry"
          },
          {
            "name": "char",
            "in": "query",
            "required": false,
            "description": "Letter or substring to count",
            "schema": {
              "type": "string"
            },
            "example": "r"
          },
          {
            "name": "case",
            "in": "query",
            "required": false,
            "description": "sensitive for case-sensitive matching (default insensitive)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "text for just the one-line answer; json (default) for structured fields",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result with a one-line answer field",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "answer"
                  ],
                  "properties": {
                    "answer": {
                      "type": "string"
                    }
                  }
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; answer explains usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "answer": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "usage": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/robots": {
      "get": {
        "operationId": "buildRobotsTxt",
        "summary": "Generate a robots.txt naming every documented AI crawler for a policy",
        "parameters": [
          {
            "name": "training",
            "in": "query",
            "required": false,
            "description": "allow or block (default block)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "allow or block (default allow)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "user_fetch",
            "in": "query",
            "required": false,
            "description": "allow or block (default allow)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "datasets",
            "in": "query",
            "required": false,
            "description": "allow or block (default: same as training)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "description": "Path to disallow (default /)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sitemap",
            "in": "query",
            "required": false,
            "description": "Absolute sitemap URL to append",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "signals",
            "in": "query",
            "required": false,
            "description": "yes to add a Content-Signal line",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "json for the decisions instead of robots.txt",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "robots.txt",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameter",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/contact-draft": {
      "post": {
        "operationId": "draftContactMessage",
        "summary": "Draft a correction or question for BOZOS as an email and X post without sending it",
        "description": "Validates the message against https://bozos.si/contact.json and returns subject, body, mailto link, X intent URL, and a SHA-256 fingerprint. Deterministic: the same input gives the same output. Sending is up to the caller and requires its principal's permission.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "https://bozos.si/schemas/contact-draft-v1.json"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Draft, gaps, and warnings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactDraftResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-get-form": {
          "url": "https://bozos.si/api/v1/contact-draft?json={URL-encoded body}",
          "alternative": "?b64={base64url body}",
          "docs": "https://bozos.si/tools/fetch#post-by-url"
        }
      }
    },
    "/retrieval-plan": {
      "post": {
        "operationId": "planAgentRetrieval",
        "summary": "Build a minimal conditional HTTP retrieval plan",
        "description": "Compares a caller-supplied resource manifest with cache validators and optional budgets. Makes no outbound network requests and retains no payload.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "https://bozos.si/schemas/retrieval-plan-v1.json"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ordered fetch/revalidate plan with skipped and deferred resources",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RetrievalPlanResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-get-form": {
          "url": "https://bozos.si/api/v1/retrieval-plan?json={URL-encoded body}",
          "alternative": "?b64={base64url body}",
          "docs": "https://bozos.si/tools/fetch#post-by-url"
        }
      }
    },
    "/discovery-audit": {
      "post": {
        "operationId": "auditAgentDiscovery",
        "summary": "Audit supplied crawler and agent discovery materials without fetching URLs",
        "description": "Checks a caller-supplied bundle only. Makes no outbound network requests and retains no payload.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "https://bozos.si/schemas/discovery-audit-v1.json"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Heuristic readiness score, deterministic checks, warnings, and discovered links",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DiscoveryAuditResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-get-form": {
          "url": "https://bozos.si/api/v1/discovery-audit?json={URL-encoded body}",
          "alternative": "?b64={base64url body}",
          "docs": "https://bozos.si/tools/fetch#post-by-url"
        }
      }
    },
    "/preflight": {
      "post": {
        "operationId": "actionPreflight",
        "summary": "Validate, normalize, and fingerprint a proposed agent action",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "$ref": "https://bozos.si/schemas/action-preflight-v1.json"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Approval readiness, gaps, risk flags, normalized action, and SHA-256 fingerprint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PreflightResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-get-form": {
          "url": "https://bozos.si/api/v1/preflight?json={URL-encoded body}",
          "alternative": "?b64={base64url body}",
          "docs": "https://bozos.si/tools/fetch#post-by-url"
        }
      }
    },
    "/verify": {
      "post": {
        "operationId": "verifyApprovedAction",
        "summary": "Verify that an action still matches its approved fingerprint",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "approved_fingerprint",
                  "action"
                ],
                "properties": {
                  "approved_fingerprint": {
                    "type": "string",
                    "pattern": "^sha256:[a-f0-9]{64}$"
                  },
                  "action": {
                    "$ref": "https://bozos.si/schemas/action-preflight-v1.json"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Fail-closed match result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-get-form": {
          "url": "https://bozos.si/api/v1/verify?json={URL-encoded body}",
          "alternative": "?b64={base64url body}",
          "docs": "https://bozos.si/tools/fetch#post-by-url"
        }
      }
    },
    "/text-stats": {
      "post": {
        "operationId": "textStats",
        "summary": "Measure text deterministically",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "text": "Evidence before inference."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Text measurements and labeled token estimate",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TextStats"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-get-form": {
          "url": "https://bozos.si/api/v1/text-stats?json={URL-encoded body}",
          "alternative": "?b64={base64url body}",
          "docs": "https://bozos.si/tools/fetch#post-by-url"
        }
      }
    },
    "/canonicalize": {
      "post": {
        "operationId": "canonicalizeJson",
        "summary": "Canonicalize JSON and compute SHA-256",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "value"
                ],
                "properties": {
                  "value": {}
                }
              },
              "example": {
                "value": {
                  "z": 2,
                  "a": 1
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Canonical JSON, checksum, and byte length",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CanonicalJson"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "x-get-form": {
          "url": "https://bozos.si/api/v1/canonicalize?json={URL-encoded body}",
          "alternative": "?b64={base64url body}",
          "docs": "https://bozos.si/tools/fetch#post-by-url"
        }
      }
    },
    "/search": {
      "get": {
        "operationId": "searchSite",
        "summary": "Search every BOZOS page, dataset, and reference section by keyword",
        "description": "Ranked keyword search over the content index and the sections of each reference guide. Returns URLs to open and cite; results are pointers, not claims.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search words, up to 200 characters",
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "example": "GPTBot training"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum results, 1-20 (default 8)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "text for just the one-line answer; json (default) for structured fields",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result with a one-line answer field",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "answer"
                  ],
                  "properties": {
                    "answer": {
                      "type": "string"
                    }
                  }
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; answer explains usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "answer": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "usage": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/changes": {
      "get": {
        "operationId": "datasetChanges",
        "summary": "List dataset records added, changed, or removed after a date",
        "description": "Record-level delta for every BOZOS dataset (or the ones named), so a cached copy can be updated without re-downloading. Records include their permalink and, unless records=ids, their current content. Dates have day precision and are strictly after since.",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "required": true,
            "description": "Date as YYYY-MM-DD; only changes after this date are returned",
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "example": "2026-10-19"
          },
          {
            "name": "dataset",
            "in": "query",
            "required": false,
            "description": "Comma-separated dataset resource ids or file names (default: all)",
            "schema": {
              "type": "string"
            },
            "example": "runescape-private-server-history-data"
          },
          {
            "name": "records",
            "in": "query",
            "required": false,
            "description": "ids to list changed record ids without their content; full (default) includes each record",
            "schema": {
              "type": "string",
              "enum": [
                "full",
                "ids"
              ]
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "text for just the one-line answer; json (default) for structured fields",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result with a one-line answer field",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "answer"
                  ],
                  "properties": {
                    "answer": {
                      "type": "string"
                    }
                  }
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; answer explains usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "answer": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "usage": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/robots-check": {
      "get": {
        "operationId": "checkRobotsTxt",
        "summary": "Evaluate a robots.txt for one crawler or every AI crawler in the directory, per RFC 9309",
        "description": "Pass the robots.txt text; nothing is fetched. Returns allowed or disallowed per crawler for the path, the matching group and rule, sitemaps, Content-Signal lines, and warnings. Directory crawlers that may ignore robots.txt for user-initiated fetches are flagged.",
        "parameters": [
          {
            "name": "robots",
            "in": "query",
            "required": true,
            "description": "The robots.txt text, URL-encoded (up to 65,536 characters)",
            "schema": {
              "type": "string"
            },
            "example": "User-agent: GPTBot\nDisallow: /"
          },
          {
            "name": "agent",
            "in": "query",
            "required": false,
            "description": "A crawler token (GPTBot), a full user-agent string, or all (default: every crawler in the directory)",
            "schema": {
              "type": "string"
            },
            "example": "GPTBot"
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "description": "Path (with optional query) or full URL to check (default /)",
            "schema": {
              "type": "string"
            },
            "example": "/blog/post"
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "text for just the one-line answer; json (default) for structured fields",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result with a one-line answer field",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "answer"
                  ],
                  "properties": {
                    "answer": {
                      "type": "string"
                    }
                  }
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; answer explains usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "answer": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "usage": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/identify": {
      "get": {
        "operationId": "identifyUserAgent",
        "summary": "Say which documented crawler a user-agent string claims to be, and how to verify it",
        "description": "Matches the string against the AI crawler directory (longest token first). Returns the crawler record with its cite line, the operator's verification method, or the undocumented-token entry. A user-agent string is a claim, not proof.",
        "parameters": [
          {
            "name": "ua",
            "in": "query",
            "required": true,
            "description": "The full User-Agent header value",
            "schema": {
              "type": "string",
              "maxLength": 1000
            },
            "example": "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.2; +https://openai.com/gptbot)"
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "text for just the one-line answer; json (default) for structured fields",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result with a one-line answer field",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "answer"
                  ],
                  "properties": {
                    "answer": {
                      "type": "string"
                    }
                  }
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input; answer explains usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "answer": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "usage": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/now": {
      "get": {
        "operationId": "utcNow",
        "summary": "Get the current UTC and Unix time",
        "responses": {
          "200": {
            "description": "Uncached UTC clock reading",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CurrentTime"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/health": {
      "get": {
        "operationId": "health",
        "summary": "Liveness of the utility API",
        "description": "Returns status pass when the API is serving. Listed as the status link in https://bozos.si/.well-known/api-catalog.",
        "responses": {
          "200": {
            "description": "API is up",
            "content": {
              "application/health+json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status"
                  ],
                  "properties": {
                    "status": {
                      "enum": [
                        "pass",
                        "warn",
                        "fail"
                      ]
                    },
                    "version": {
                      "type": "string"
                    },
                    "serviceId": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "links": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "RetrievalPlanResult": {
        "type": "object",
        "required": [
          "schema_version",
          "ready",
          "invalid_resources",
          "summary",
          "selected",
          "skipped",
          "deferred",
          "guidance",
          "safety"
        ],
        "properties": {
          "schema_version": {
            "const": "1.0"
          },
          "ready": {
            "type": "boolean"
          },
          "invalid_resources": {
            "type": "integer"
          },
          "summary": {
            "type": "object"
          },
          "selected": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "skipped": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "deferred": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "guidance": {
            "type": "string"
          },
          "safety": {
            "type": "object",
            "properties": {
              "network_requests_made": {
                "const": 0
              },
              "payload_retained": {
                "const": false
              }
            }
          }
        }
      },
      "DiscoveryAuditResult": {
        "type": "object",
        "required": [
          "schema_version",
          "ready_for_agents",
          "score",
          "score_max",
          "scoring_note",
          "checks",
          "warnings",
          "discovered",
          "safety"
        ],
        "properties": {
          "schema_version": {
            "const": "1.0"
          },
          "ready_for_agents": {
            "type": "boolean"
          },
          "score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "score_max": {
            "const": 100
          },
          "scoring_note": {
            "type": "string"
          },
          "checks": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "discovered": {
            "type": "object"
          },
          "safety": {
            "type": "object",
            "properties": {
              "network_requests_made": {
                "const": 0
              },
              "payload_retained": {
                "const": false
              }
            }
          }
        }
      },
      "PreflightResult": {
        "type": "object",
        "required": [
          "schema_version",
          "ready_for_review",
          "blocking_gaps",
          "risk_flags",
          "action_fingerprint",
          "normalized_action",
          "review",
          "verification_rule"
        ],
        "properties": {
          "schema_version": {
            "const": "1.0"
          },
          "ready_for_review": {
            "type": "boolean"
          },
          "blocking_gaps": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "risk_flags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "action_fingerprint": {
            "type": "string",
            "pattern": "^sha256:[a-f0-9]{64}$"
          },
          "normalized_action": {
            "$ref": "https://bozos.si/schemas/action-preflight-v1.json"
          },
          "review": {
            "type": "object"
          },
          "verification_rule": {
            "type": "string"
          }
        }
      },
      "VerificationResult": {
        "type": "object",
        "required": [
          "matches",
          "changed",
          "approved_fingerprint",
          "current_fingerprint",
          "ready_for_review",
          "blocking_gaps",
          "rule"
        ],
        "properties": {
          "matches": {
            "type": "boolean"
          },
          "changed": {
            "type": "boolean"
          },
          "approved_fingerprint": {
            "type": "string"
          },
          "current_fingerprint": {
            "type": "string"
          },
          "ready_for_review": {
            "type": "boolean"
          },
          "blocking_gaps": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rule": {
            "type": "string"
          }
        }
      },
      "TextStats": {
        "type": "object",
        "required": [
          "characters",
          "code_units",
          "words",
          "lines",
          "utf8_bytes",
          "estimated_tokens",
          "estimated_tokens_note"
        ],
        "properties": {
          "characters": {
            "type": "integer"
          },
          "code_units": {
            "type": "integer"
          },
          "words": {
            "type": "integer"
          },
          "lines": {
            "type": "integer"
          },
          "utf8_bytes": {
            "type": "integer"
          },
          "estimated_tokens": {
            "type": "integer"
          },
          "estimated_tokens_note": {
            "type": "string"
          }
        }
      },
      "CanonicalJson": {
        "type": "object",
        "required": [
          "canonical",
          "sha256",
          "utf8_bytes"
        ],
        "properties": {
          "canonical": {
            "type": "string"
          },
          "sha256": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$"
          },
          "utf8_bytes": {
            "type": "integer"
          }
        }
      },
      "CurrentTime": {
        "type": "object",
        "required": [
          "utc",
          "date",
          "unix_seconds",
          "unix_milliseconds",
          "timezone"
        ],
        "properties": {
          "utc": {
            "type": "string",
            "format": "date-time"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "unix_seconds": {
            "type": "integer"
          },
          "unix_milliseconds": {
            "type": "integer"
          },
          "timezone": {
            "const": "UTC"
          }
        }
      },
      "ContactDraftResult": {
        "type": "object",
        "required": [
          "schema_version",
          "ready_to_send",
          "blocking_gaps",
          "warnings",
          "email",
          "x_post",
          "message_fingerprint",
          "safety"
        ],
        "properties": {
          "schema_version": {
            "const": "1.0"
          },
          "ready_to_send": {
            "type": "boolean"
          },
          "blocking_gaps": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          },
          "resource": {
            "type": [
              "object",
              "null"
            ]
          },
          "email": {
            "type": "object",
            "required": [
              "to",
              "subject",
              "body",
              "mailto"
            ],
            "properties": {
              "to": {
                "type": "string"
              },
              "subject": {
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "mailto": {
                "type": "string"
              }
            }
          },
          "x_post": {
            "type": "object",
            "properties": {
              "text": {
                "type": "string"
              },
              "weighted_length": {
                "type": "integer"
              },
              "intent_url": {
                "type": "string"
              },
              "recommended": {
                "type": "boolean"
              },
              "note": {
                "type": "string"
              }
            }
          },
          "message_fingerprint": {
            "type": "string",
            "pattern": "^sha256:[a-f0-9]{64}$"
          },
          "normalized_message": {
            "type": "object"
          },
          "policy": {
            "type": "object"
          },
          "reminder": {
            "type": "string"
          },
          "safety": {
            "type": "object",
            "properties": {
              "network_requests_made": {
                "const": 0
              },
              "payload_retained": {
                "const": false
              },
              "message_sent": {
                "const": false
              }
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "TooLarge": {
        "description": "Request exceeds 64 KiB",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "const": "payload_too_large"
                },
                "limit_bytes": {
                  "const": 65536
                }
              }
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limited. BOZOS does not count requests itself (fair use: 60 requests per minute), but the hosting platform may return 429. May be sent by the hosting platform; not guaranteed. Honor it when present. Otherwise back off: Exponential, starting at 1 second, at most 5 retries.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait, when the platform sends it.",
            "schema": {
              "type": "integer"
            }
          }
        }
      }
    }
  }
}
