{
  "openapi": "3.1.0",
  "info": {
    "title": "tokenusage.dev public data API",
    "version": "1.0.0",
    "description": "Read-only reset announcements and Nerf Watch scores. No key. Public display requires Data: tokenusage.dev linked to https://tokenusage.dev/resets for resets or https://tokenusage.dev/nerfwatch for scores on the same screen or in the same message. Personal use needs no visible credit. Relays must pass on the credit URL and condition. A lower score on this suite is not proof that a provider changed a model. Response fields may be added.",
    "termsOfService": "https://tokenusage.dev/terms",
    "contact": {
      "name": "tokenusage.dev",
      "email": "support@tokenusage.dev",
      "url": "https://tokenusage.dev/contact"
    }
  },
  "servers": [
    { "url": "https://tokenusage.dev" }
  ],
  "security": [],
  "externalDocs": {
    "description": "Fields, caching and reuse conditions",
    "url": "https://tokenusage.dev/developers"
  },
  "paths": {
    "/api/resets": {
      "description": "At most the 50 newest visible announcements recorded from @thsottiaux, newest postedAt first; not a complete history. A post is not proof that any account's limit was reset. Use GET or HEAD without credentials or custom request headers. Poll no faster than every 15 seconds. Unsupported methods return the documented 405 error.",
      "get": {
        "operationId": "getResetAnnouncements",
        "summary": "Read recorded Codex reset announcements",
        "responses": {
          "200": { "$ref": "#/components/responses/ResetJson" },
          "405": { "$ref": "#/components/responses/ResetMethodNotAllowed" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "head": {
        "operationId": "headResetAnnouncements",
        "summary": "Read reset JSON response headers",
        "responses": {
          "200": { "$ref": "#/components/responses/ResetHead" },
          "405": { "$ref": "#/components/responses/HeadError" },
          "500": { "$ref": "#/components/responses/HeadError" }
        }
      }
    },
    "/api/resets.atom": {
      "description": "Atom 1.0 for the same recorded announcements as /api/resets, in the same order. Entry ids and alternate links are source-post URLs. No post text is included. Retain the feed rights statement and each entry's linked credit when relaying Atom. Use GET or HEAD without credentials or custom request headers. Poll no faster than every 15 seconds. Unsupported methods return the documented 405 error.",
      "get": {
        "operationId": "getResetAnnouncementFeed",
        "summary": "Read the Codex reset announcement Atom feed",
        "responses": {
          "200": { "$ref": "#/components/responses/ResetAtom" },
          "405": { "$ref": "#/components/responses/ResetMethodNotAllowed" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "head": {
        "operationId": "headResetAnnouncementFeed",
        "summary": "Read Atom response headers",
        "responses": {
          "200": { "$ref": "#/components/responses/ResetHead" },
          "405": { "$ref": "#/components/responses/HeadError" },
          "500": { "$ref": "#/components/responses/HeadError" }
        }
      }
    },
    "/api/nerfwatch": {
      "description": "Each model's latest score, previous run, changes, baseline and history, ordered by provider, name and model id. Missing days are missing, not zero. Comparisons use one suite version. A lower score is a lower score on this suite, not proof that a provider changed a model. Use GET or HEAD without credentials or custom request headers. Poll no faster than every 5 minutes. evidenceAvailable and private evidence endpoints are outside this stable reuse contract. Unsupported methods return the documented 405 error.",
      "get": {
        "operationId": "getNerfWatchScores",
        "summary": "Read Nerf Watch scores and history",
        "responses": {
          "200": { "$ref": "#/components/responses/NerfwatchJson" },
          "405": { "$ref": "#/components/responses/NerfwatchMethodNotAllowed" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "head": {
        "operationId": "headNerfWatchScores",
        "summary": "Read Nerf Watch response headers",
        "responses": {
          "200": { "$ref": "#/components/responses/NerfwatchHead" },
          "405": { "$ref": "#/components/responses/HeadError" },
          "500": { "$ref": "#/components/responses/HeadError" }
        }
      }
    }
  },
  "components": {
    "headers": {
      "ResetCache": {
        "schema": {
          "type": "string",
          "const": "public, max-age=0, s-maxage=15"
        }
      },
      "NerfwatchCache": {
        "schema": {
          "type": "string",
          "const": "public, max-age=60, s-maxage=300"
        }
      },
      "NoStore": {
        "schema": { "type": "string", "const": "no-store" }
      },
      "PublicCors": {
        "description": "Successful public reads only; no credentialed CORS.",
        "schema": { "type": "string", "const": "*" }
      },
      "Noindex": {
        "schema": { "type": "string", "const": "noindex" }
      }
    },
    "responses": {
      "ResetJson": {
        "description": "Recorded announcements. Additional response fields are the site's own interface unless documented as stable.",
        "headers": {
          "Cache-Control": { "$ref": "#/components/headers/ResetCache" },
          "Access-Control-Allow-Origin": { "$ref": "#/components/headers/PublicCors" },
          "X-Robots-Tag": { "$ref": "#/components/headers/Noindex" }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ResetsResponse" }
          }
        }
      },
      "ResetAtom": {
        "description": "Atom 1.0, UTF-8. Feed updated is the greatest returned detectedAt, or generatedAt when empty. Each entry uses postedAt for published and detectedAt for updated. This is a current window without deletion tombstones or a monotonic change cursor.",
        "headers": {
          "Cache-Control": { "$ref": "#/components/headers/ResetCache" },
          "Access-Control-Allow-Origin": { "$ref": "#/components/headers/PublicCors" },
          "X-Robots-Tag": { "$ref": "#/components/headers/Noindex" }
        },
        "content": {
          "application/atom+xml": {
            "schema": { "type": "string" }
          }
        }
      },
      "NerfwatchJson": {
        "description": "Public scores and metadata. Additional response fields are the site's own interface unless documented as stable.",
        "headers": {
          "Cache-Control": { "$ref": "#/components/headers/NerfwatchCache" },
          "Access-Control-Allow-Origin": { "$ref": "#/components/headers/PublicCors" },
          "X-Robots-Tag": { "$ref": "#/components/headers/Noindex" }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/NerfwatchResponse" }
          }
        }
      },
      "ResetHead": {
        "description": "The selected reset path's GET headers, with no transmitted body. Content-Type is application/json for /api/resets and application/atom+xml; charset=utf-8 for /api/resets.atom.",
        "headers": {
          "Cache-Control": { "$ref": "#/components/headers/ResetCache" },
          "Access-Control-Allow-Origin": { "$ref": "#/components/headers/PublicCors" },
          "X-Robots-Tag": { "$ref": "#/components/headers/Noindex" }
        }
      },
      "NerfwatchHead": {
        "description": "GET headers, with no transmitted body.",
        "headers": {
          "Cache-Control": { "$ref": "#/components/headers/NerfwatchCache" },
          "Access-Control-Allow-Origin": { "$ref": "#/components/headers/PublicCors" },
          "X-Robots-Tag": { "$ref": "#/components/headers/Noindex" }
        }
      },
      "InternalError": {
        "description": "The read failed. No CORS headers are sent.",
        "headers": {
          "Cache-Control": { "$ref": "#/components/headers/NoStore" },
          "X-Robots-Tag": { "$ref": "#/components/headers/Noindex" }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": "internal",
              "message": "something went wrong on our side"
            }
          }
        }
      },
      "ResetMethodNotAllowed": {
        "description": "Unsupported method on either reset path. No CORS headers are sent.",
        "headers": {
          "Cache-Control": { "$ref": "#/components/headers/NoStore" },
          "X-Robots-Tag": { "$ref": "#/components/headers/Noindex" }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": "method_not_allowed",
              "message": "GET or HEAD"
            }
          }
        }
      },
      "NerfwatchMethodNotAllowed": {
        "description": "Unsupported method. The existing method message remains GET. No CORS headers are sent.",
        "headers": {
          "Cache-Control": { "$ref": "#/components/headers/NoStore" },
          "X-Robots-Tag": { "$ref": "#/components/headers/Noindex" }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": "method_not_allowed",
              "message": "GET"
            }
          }
        }
      },
      "HeadError": {
        "description": "Error headers with no transmitted body and no CORS headers.",
        "headers": {
          "Cache-Control": { "$ref": "#/components/headers/NoStore" },
          "X-Robots-Tag": { "$ref": "#/components/headers/Noindex" }
        }
      }
    },
    "schemas": {
      "ResetsResponse": {
        "type": "object",
        "additionalProperties": true,
        "required": ["source", "generatedAt", "lastPolledAt", "resets"],
        "properties": {
          "source": {
            "type": "string",
            "const": "https://tokenusage.dev/resets",
            "description": "Fixed reset credit URL."
          },
          "generatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Response generation time, not data modification time."
          },
          "lastPolledAt": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "Latest successful timeline fetch and persistence checkpoint, including checks with no new posts; null before the first or when a legacy record lacks it. Not a classification or delivery heartbeat."
          },
          "resets": {
            "type": "array",
            "maxItems": 50,
            "items": { "$ref": "#/components/schemas/ResetPublicItem" }
          }
        }
      },
      "ResetPublicItem": {
        "type": "object",
        "additionalProperties": true,
        "required": ["postId", "url", "postedAt", "detectedAt", "resetType"],
        "properties": {
          "postId": { "type": "string" },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Source-post URL."
          },
          "postedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the source post was published."
          },
          "detectedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the monitor recorded the announcement."
          },
          "resetType": {
            "type": ["string", "null"],
            "enum": ["automatic", "banked", "both", null],
            "description": "Monitor classification: automatic is a direct usage-limit reset with no saved reset; banked is a grant saved for eligible users to apply later and does not itself refill limits; both includes both types. Null means recorded before reset types were recorded. Not proof that any account's limit was reset."
          }
        }
      },
      "NerfwatchResponse": {
        "type": "object",
        "additionalProperties": true,
        "required": [
          "generatedAt", "dataUpdatedAt", "historyDays", "baselineDays",
          "minBaselineRuns", "flatPoints", "models"
        ],
        "properties": {
          "generatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Response generation time."
          },
          "dataUpdatedAt": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "When the newest retained run was stored; null without runs."
          },
          "historyDays": {
            "type": "integer",
            "minimum": 1,
            "examples": [366],
            "description": "History retention bound per model."
          },
          "baselineDays": {
            "type": "integer",
            "minimum": 1,
            "examples": [7],
            "description": "UTC-day window before the latest run used for its baseline."
          },
          "minBaselineRuns": {
            "type": "integer",
            "minimum": 1,
            "examples": [3],
            "description": "Minimum same-suite runs needed for a baseline."
          },
          "flatPoints": {
            "type": "number",
            "minimum": 0,
            "examples": [2],
            "description": "Absolute change versus baseline at or below this value is flat."
          },
          "models": {
            "type": "array",
            "description": "Ordered by provider, name and model id; empty before the first run.",
            "items": { "$ref": "#/components/schemas/NerfwatchModel" }
          }
        }
      },
      "NerfwatchModel": {
        "type": "object",
        "additionalProperties": true,
        "required": [
          "model", "name", "provider", "latest", "previous", "change",
          "baseline", "baselineRuns", "changeVsBaseline", "trend", "history"
        ],
        "properties": {
          "model": { "type": "string" },
          "name": { "type": "string" },
          "provider": { "type": "string" },
          "latest": {
            "description": "Newest run, or null.",
            "anyOf": [
              { "$ref": "#/components/schemas/NerfwatchPoint" },
              { "type": "null" }
            ]
          },
          "previous": {
            "description": "Run before the latest, whatever its day, or null.",
            "anyOf": [
              { "$ref": "#/components/schemas/NerfwatchPoint" },
              { "type": "null" }
            ]
          },
          "change": {
            "type": ["number", "null"],
            "minimum": -100,
            "maximum": 100,
            "description": "Latest minus previous, in points, rounded to two decimals; null without a previous run on the same suite version."
          },
          "baseline": {
            "type": ["number", "null"],
            "minimum": 0,
            "maximum": 100,
            "description": "Mean of same-suite scores in the baseline window, rounded to two decimals; null with too few runs."
          },
          "baselineRuns": {
            "type": "integer",
            "minimum": 0,
            "description": "Number of same-suite runs in the baseline window, even when too few for a baseline."
          },
          "changeVsBaseline": {
            "type": ["number", "null"],
            "minimum": -100,
            "maximum": 100,
            "description": "Latest minus baseline, in points, rounded to two decimals; null without a baseline."
          },
          "trend": {
            "type": ["string", "null"],
            "enum": ["down", "flat", "up", null],
            "description": "Direction against the baseline using flatPoints; null without a baseline."
          },
          "history": {
            "type": "array",
            "description": "Oldest first. Days without a run are missing, not zero.",
            "items": { "$ref": "#/components/schemas/NerfwatchPoint" }
          }
        }
      },
      "NerfwatchPoint": {
        "type": "object",
        "additionalProperties": true,
        "required": ["day", "score", "suiteVersion"],
        "properties": {
          "day": {
            "type": "string",
            "format": "date",
            "description": "UTC day."
          },
          "score": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Percent score on this suite, rounded to two decimals."
          },
          "suiteVersion": {
            "type": "string",
            "description": "Comparisons are made only within one suite version."
          }
        }
      },
      "Error": {
        "type": "object",
        "additionalProperties": true,
        "required": ["error", "message"],
        "properties": {
          "error": { "type": "string" },
          "message": { "type": "string" }
        }
      }
    }
  }
}
